[bzoj4516][Sdoi2016]生成魔咒

真后缀自动机裸题。

不断在字符串后面加入数字。每次操作求子串数量,去重。

显然$ans=\sum len[u]-len[fa[u]]$。每次更改或新建了$fa$就去更新一下$ans$。

推一下式子就会发现复制节点的操作只是贡献了$len[copy]-len[np]$,就更妙了。

因为是$1e9$的字符集,要用map的。。(copy更好写了)

#include<bits/stdc++.h>
using namespace std;
const int N=200010;
typedef long long ll;
inline int read(){
    int r=0,c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c))
    r=r*10+c-'0',c=getchar();
    return r;
}
int len[N],fa[N];
map<int,int>ch[N];
int sz,las;ll ans=0;
void ins(int c){
    int now=++sz;len[now]=len[las]+1;
    int p,q;
    for(p=las;~p&&!ch[p][c];p=fa[p])
    ch[p][c]=now;
    if(!~p){
        fa[now]=0;ans+=len[now];
    }
    else{
        q=ch[p][c];
        if(len[q]==len[p]+1){
            fa[now]=q;ans+=len[now]-len[q];
        }
        else{
            int r=++sz;
            fa[r]=fa[q],len[r]=len[p]+1;
            ch[r]=ch[q];
            for(;~p&&ch[p][c]==q;p=fa[p])
            ch[p][c]=r;
            fa[q]=fa[now]=r;ans+=len[now]-len[r];
        }
    }
    las=now;
    printf("%lld\n",ans);
}
int main(){
    int n=read();fa[0]=-1;
    for(int i=1;i<=n;i++)
    ins(read());
}

 

posted @ 2018-01-18 10:58  orzzz  阅读(185)  评论(0编辑  收藏  举报