世界線(bzoj2894)(廣義字尾自動機)
由於春希對於第二世代操作的不熟練,所以剛使用完\(invasion process\)便掉落到了世界線之外,錯綜複雜的平行世界資訊湧入到春希的意識中。春希明白了事件的真相。
在一個冬馬與雪菜同時存在的世界裡,傲嬌的冬馬最終還是博得了春希的內心。然而看著好友雪菜的消瘦,內心愧疚的冬馬啟動了第二世代操作,想找到一個雪菜最終成功的世界,卻發現哪裡都沒有。絕望的冬馬決定耗盡自己全部的第二世代操作點數,自創一個沒有自己只有雪菜與春希的世界。
雖然這個世界一開始效果很好,春希與雪菜很快的被命運撮合在了一起,然而沒有了冬馬的雪菜,如沒有了大海的沙灘,失去了傍依。
雖然世界裡沒有冬馬的存在,但是由於冬馬創造時的疏忽,這個世界裡的雪菜依然存在著因獨佔春希而產生的對冬馬的愧疚感,這種愧疚感折磨著雪菜,最終雪菜選擇了自毀忘記春希。
看著這一切的春希深知不管是三個人一起的快樂,還是兩個人獨處的甜蜜,都無法消除冬馬與雪菜內心的自責,無論如何修改世界,三人都只會更加痛苦,於是春希使用了自己剩餘的全部操作點數,念出了\(key world:WhiteAlbum2\)
在\(initialization process\)中,春希需要整理世界線,才能迴歸原本的世界。
世界線是一棵根節點為1的樹,每個節點為1個字元。規定樹上的子串為從某個節點(不一定是1號節點)出發往其子節點走所形成的字串。每一個子串相當於一個平行世界,要想重構世界,就需要知道兩個資訊:
- 不同子串的個數
- 將不同的子串排序後,字典序第\(k-1\)小的子串。
如圖所示為一個世界線的樣例,從\(4->5\)的子串為\(bb\),\(1->5\)的為\(abb\)
Input
第一行兩個整數\(n\),\(q\)
第二行\(n\)個字元,表示編號為\(i\)的字元是什麼。
接下來\(n-1\)行表示一棵樹。
接下來\(q\)行,每行一個整數\(k\)
Output
第一行為不同支付串個數。
接下來q行為q個詢問的答案(注意輸出的是第\(k-1\)小的子串,如果\(k=1\)請直接換行),如果不存在(不包括\(k=1\))輸出\(-1\).
Sample Input
8 1
abcbbaca
1 2
2 3
1 4
4 5
4 6
4 7
1 8
5
Sample Output
12
aba
Hint
12%:\(n<=10\)
另有48%為一條鏈;
100%: \(n<=250000\)
題意:
給出一棵\(trie\)樹,求出上面所有字串的本質不同的子串的總數(算上空串),並求出其中的第\(k\)大子串。
題解:
給每個節點記錄一個\(last\),每次加入字元的時候把\(last\)設為其父節點記錄的\(last\),然後就可以像普通的字尾自動機一樣建了。最後找第\(k\)小的子串如這個就行了
#include<bits/stdc++.h>
using namespace std;
const int N=600010;
int n,q;
int tot,bian[N<<1],nxt[N<<1],head[N];
inline void add(int x,int y){
tot++,bian[tot]=y,nxt[tot]=head[x],head[x]=tot;
}
char s[N];
int a[N],c[N];
struct SAM{
int lst[N],last,cnt,now;
long long size[N];
int ch[N][26],fa[N<<1],l[N<<1];
void ins(int c){
if(ch[last][c]&&l[ch[last][c]]==l[last]+1){
last=ch[last][c];
return;
}
int p=last,np=++cnt;last=np;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(l[p]+1==l[q])fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[q]);
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
size[nq]=1;
}
}
size[np]=1;
}
void dfs(int x,int f){
last=lst[f];
now=x;
ins(s[x]-'a');
lst[x]=last;
for(int i=head[x];i;i=nxt[i]){
int y=bian[i];
if(f==bian[i])continue;
dfs(y,x);
last=lst[x];
}
}
void build(){
lst[0]=1;cnt=1;
dfs(1,0);
}
void calc(){
int ans=0;
for(int i=1;i<=cnt;++i)c[l[i]]++;
for(int i=1;i<=cnt;++i)c[i]+=c[i-1];
for(int i=1;i<=cnt;++i)a[c[l[i]]--]=i;
for(int i=cnt;i;--i){
int p=a[i];
for(int j=0;j<26;++j){
if(ch[p][j])size[p]+=size[ch[p][j]];
}
}
}
void find(int k){
int p=1;
while(k){
int a=0;
while(k>size[ch[p][a]]&&a<26){
if (ch[p][a]) k-=size[ch[p][a]];
a++;
}
putchar('a'+a);k--;
p=ch[p][a];
}
}
}sam;
int main(){
cin>>n>>q;
cin>>s+1;
for(int i=1;i<n;++i){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
sam.build();
sam.calc();
long long sm;
cout<<(sm=sam.size[1]+1)<<endl;
while(q--){
long long k;
scanf("%lld",&k);
if(k==1){
puts("");
}else{
if(k>sm)puts("-1");
else sam.find(k-1),putchar('\n');
}
}
}