[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()); }