【2018/10/16測試T3】長者
阿新 • • 發佈:2018-11-09
【題目】
【分析】
30 pts:雜湊+二分
可以用
排序,也可以利用雜湊做到 O
比較兩個字串的大小。
具體方法是雜湊結合二分找到第一個字串不同的位置,然後比較這個位置的大小。
結合 ,時間複雜度O 。
另外 30 pts( ):二叉樹
此時形成一棵二叉樹,任意長者的字串與 號長者的字串最多隻有 O 處不同。這說明任意兩個字串最多也只有 O 處不同。
於是我們只用比較這 O 個不同的位置,結合 可以做到 O 。
100 pts:主席樹
雜湊二分的方法其實就是不斷的詢問字串的某一部分的雜湊值。
我們可以用線段樹來維護雜湊值,那麼每次修改一個位置只需要把父親的線段樹改一個位置。 利用主席樹來維護,比較的時候也從主席樹的兩個根開始遞迴比較,可以做到 O 。
這裡解釋一點:程式碼中線段樹在合併雜湊值時是倒著合併的,即若左子樹是
,右子樹是
,那合併上來應該是
,但這不影響答案的正確性,因為雜湊只是判斷兩個串是否相等,比較時還是要遞迴到葉子才
(改成正著合併也可以)
【程式碼】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define LogN 40
#define base 31
#define ull unsigned long long
using namespace std;
int n,m,t;
char s[N];
ull Pow[N]={1};
int rank[N],root[N];
struct President_Tree
{
ull num;
int lc,rc,len;
#define lc(x) a[x].lc
#define rc(x) a[x].rc
#define len(x) a[x].len
#define num(x) a[x].num
}a[N*LogN];
void build(int &root,int l,int r)
{
root=++t;
len(root)=r-l+1;
if(l==r)
{
num(root)=s[l];
return;
}
int mid=(l+r)>>1;
build(lc(root),l,mid);
build(rc(root),mid+1,r);
num(root)=num(lc(root))+num(rc(root))*Pow[len(lc(root))];
}
void insert(int y,int &x,int l,int r,int pos,char c)
{
x=++t;
a[x]=a[y];
if(l==r)
{
num(x)=c;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) insert(lc(y),lc(x),l,mid,pos,c);
else insert(rc(y),rc(x),mid+1,r,pos,c);
num(x)=num(lc(x))+num(rc(x))*Pow[len(lc(x))];
}
int cmp(int x,int y,int l,int r)
{
if(l==r)
return num(x)<num(y)?-1:1;
int mid=(l+r)>>1;
if(num(lc(x))!=num(lc(y)))
return cmp(lc(x),lc(y),l,mid);
return cmp(rc(x),rc(y),mid+1,r);
}
bool comp(const int &p,const int &q)
{
if(num(root[p])!=num(root[q]))
return cmp(root[p],root[q],1,m)<0;
return p<q;
}
int main()
{
// freopen("z.in","r",stdin);
// freopen("z.out","w",stdout);
int p,pos,i;
scanf("%d%d%s",&n,&m,s+1);
for(i=1;i<=m;++i)
Pow[i]=Pow[i-1]*base;
build(root[1],1,m);
for(i=2;i<=n;++i)
{
scanf("%d%d%s",&p,&pos,s+1);
insert(root[p],root[i],1,m,pos,s[1]);
}
for(i=1;i<=n;++i) rank[i]=i;
sort(rank+1,rank+n+1,comp);
for(i=1;i<=n;++i) printf("%d ",rank[i]);
// fclose(stdin);
// fclose(stdout);
return 0;
}