P4248 [AHOI2013]差异
题目大意
长度为 $n$的字符串$S$,令$T_i$表示它从第$i$个字符开始的后缀,求$\sum\limits_{1\leqslant i<j\leqslant n}^{} len(T_i)+len(T_j)+2×lcp(T_i+T_j)$
其中,$lcp(a,b)$表示$a$和$b$的最长公共前缀
Ps:$T_i$表示第i个字符开始的后缀,注意是开始,$juruo$看了半天样例都看不懂$emmm$
前半部分显然能化简成$\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^ni+j$ $=$ $(n-1)$ $×$ $\sum\limits_{i=1}^ni$ $=$$\frac{(n-1)×n×(n+1)}{2}$
至于后半部分:
后缀的最长公共前缀等价于反串前缀的最长公共后缀
构造出反串的$parent$树,根据父子关系dp出每个后缀的累加次数
My complete code:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const LL maxn=2000000; LL Len,last=1,nod=1,ans; LL len[maxn],fail[maxn],son[maxn][26],val[maxn],t[maxn],a[maxn]; char s[maxn]; inline void Insert(LL c){ LL p=last,np=++nod; last=np; len[np]=len[p]+1; val[np]=1; while(p&&!son[p][c]){ son[p][c]=np; p=fail[p]; } if(!p) fail[np]=1; else{ LL q=son[p][c]; if(len[q]==len[p]+1) fail[np]=q; else{ LL nq=++nod; len[nq]=len[p]+1; memcpy(son[nq],son[q],sizeof(son[q])); fail[nq]=fail[q]; fail[q]=fail[np]=nq; while(p&&son[p][c]==q){ son[p][c]=nq; p=fail[p]; } } } } int main(){ scanf(" %s",s+1); Len=strlen(s+1); for(LL i=Len;i>=1;--i) Insert(s[i]-'a'); for(LL i=1;i<=nod;++i) ++t[len[i]]; for(LL i=1;i<=nod;++i) t[i]+=t[i-1]; for(LL i=1;i<=nod;++i) a[t[len[i]]--]=i; for(LL i=nod;i>=1;--i) val[fail[a[i]]]+=val[a[i]]; ans=(Len+1)*(Len-1)*Len/2; for(int i=2;i<=nod;++i) ans-=(val[i]-1)*val[i]*(len[i]-len[fail[i]]); printf("%lld",ans); return 0; }