bzoj 4516 生成魔咒 Posted on 2019-06-18 | Views: $SAM$ . 每次在末尾加入一个字符,再询问当前串本质不同的子串数目. 用 $SAM$ 维护,答案就是所有非根结点的 $maxlen-minlen+1$ 之和,即 $maxlen(u)-maxlen(fa_u)$ 之和. 由于字符集比较大,所以用 $map$ 存边,确定/更换父亲结点时更新答案即可. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162#include<bits/stdc++.h>using namespace std;typedef long long ll;inline int read(){ int out=0,fh=1; char jp=getchar(); while ((jp>'9'||jp<'0')&&jp!='-') jp=getchar(); if (jp=='-') fh=-1,jp=getchar(); while (jp>='0'&&jp<='9') out=out*10+jp-'0',jp=getchar(); return out*fh;}const int MAXN=2e5+10;struct SuffixAutomaton{ map<int,int> ch[MAXN]; int idx,lst; int len[MAXN],fa[MAXN]; ll ans; SuffixAutomaton(){idx=lst=1;ans=0;memset(fa,0,sizeof fa);} void Extend(int c) { int p=lst,np=++idx; lst=np; len[np]=len[p]+1; while(p && ch[p][c]==0) ch[p][c]=np,p=fa[p]; if(p==0) fa[np]=1,ans+=len[np]; else { int q=ch[p][c]; if(len[q]==len[p]+1) { fa[np]=q; ans+=len[np]-len[q]; } else { int nq=++idx; len[nq]=len[p]+1; fa[nq]=fa[q]; fa[q]=fa[np]=nq; ans+=len[np]-len[nq]; ch[nq]=ch[q]; while(p && ch[p][c]==q) ch[p][c]=nq,p=fa[p]; } } printf("%lld\n",ans); }}SAM;int main(){ int n=read(); for(int i=1;i<=n;++i) SAM.Extend(read()); return 0;}