[AHOI2013]差异 后缀自动机_Parent树
题中要求:
$\sum_{1\leqslant i < j \leq n } Len(T_{i}) +Len(T_{j})-2LCP(T_{i},T_{j})$
公式左边的部分很好求,是一个常量,关键在于如何求取右边的 $2*LCP(T_{i},T_{j})$
在后缀自动机中,任意两个字符串所代表的节点在 $Parent$ 树中的公共祖先所代表的字符串一
定为两个字符串的最长公共后缀,
我们想求最长公共前缀,将字符串倒着插入即可.
一次考虑每个点作为公共祖先能贡献的值:
我们要使答案不重复,不遗漏的计算到. 即任意两个串的 LCP 必须只算一次.
可以一边更新每个点的 right 数组大小,一边计算该点父亲的贡献.
细节可以自己思考.
Code:
#include <cstdio> #include <algorithm> #include <cstring> #define setIO(s) freopen(s".in","r",stdin) #define maxn 1000000 #define N 30 #define ll long long using namespace std; int last=1,tot=1,n; int ch[maxn][N],cnt[maxn],f[maxn],dis[maxn],rk[maxn]; ll C[maxn],ans; char str[maxn]; struct Suffix_Automaton{ void ins(int c){ int p=last,np=++tot; last=np; dis[np]=dis[p]+1; while(p&&!ch[p][c])ch[p][c]=np,p=f[p]; if(!p) f[np]=1; else{ int q=ch[p][c],nq; if(dis[q]==dis[p]+1) f[np]=q; else{ nq=++tot; dis[nq]=dis[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); f[nq]=f[q],f[q]=f[np]=nq; while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p]; } } cnt[last]=1; } }sam; int main(){ //s//etIO("input"); scanf("%s",str),n=strlen(str); for(int i=n-1;i>=0;--i) sam.ins(str[i]-'a'); for(int i=1;i<=tot;++i) ++C[dis[i]]; for(int i=1;i<=tot;++i) C[i]+=C[i-1]; for(int i=1;i<=tot;++i) rk[C[dis[i]]--]=i; for(int i = 1;i <= n; ++i) ans += (long long)i * (n - 1); for(int i=tot;i>=1;--i){ int p=rk[i]; ans -= (long long)2 * dis[f[p]] * cnt[p] * cnt[f[p]]; cnt[f[p]]+=cnt[p]; } printf("%lld",ans); return 0; }