1. 程式人生 > >世界線(bzoj2894)(廣義字尾自動機)

世界線(bzoj2894)(廣義字尾自動機)

由於春希對於第二世代操作的不熟練,所以剛使用完\(invasion process\)便掉落到了世界線之外,錯綜複雜的平行世界資訊湧入到春希的意識中。春希明白了事件的真相。
在一個冬馬與雪菜同時存在的世界裡,傲嬌的冬馬最終還是博得了春希的內心。然而看著好友雪菜的消瘦,內心愧疚的冬馬啟動了第二世代操作,想找到一個雪菜最終成功的世界,卻發現哪裡都沒有。絕望的冬馬決定耗盡自己全部的第二世代操作點數,自創一個沒有自己只有雪菜與春希的世界。
雖然這個世界一開始效果很好,春希與雪菜很快的被命運撮合在了一起,然而沒有了冬馬的雪菜,如沒有了大海的沙灘,失去了傍依。
雖然世界裡沒有冬馬的存在,但是由於冬馬創造時的疏忽,這個世界裡的雪菜依然存在著因獨佔春希而產生的對冬馬的愧疚感,這種愧疚感折磨著雪菜,最終雪菜選擇了自毀忘記春希。
看著這一切的春希深知不管是三個人一起的快樂,還是兩個人獨處的甜蜜,都無法消除冬馬與雪菜內心的自責,無論如何修改世界,三人都只會更加痛苦,於是春希使用了自己剩餘的全部操作點數,念出了\(key world:WhiteAlbum2\)

,開始了\(initialization process\).
\(initialization process\)中,春希需要整理世界線,才能迴歸原本的世界。
世界線是一棵根節點為1的樹,每個節點為1個字元。規定樹上的子串為從某個節點(不一定是1號節點)出發往其子節點走所形成的字串。每一個子串相當於一個平行世界,要想重構世界,就需要知道兩個資訊:

  1. 不同子串的個數
  2. 將不同的子串排序後,字典序第\(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\)

,\(q<=50000\).

題意:

給出一棵\(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');
        }
    }
}