AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3238php
发现前面的len(Ti)+len(Tj)都是定值,因此其实咱们的目标就是求出lcp(Ti,Tj)来.ide
lcp说实话就是一段子串对吧,可是不太好找.因此咱们能够把串反过来.spa
这样的话,原图中后缀的最长公共前缀就变成了新串中前缀的最长公共后缀.code
这样的话提到了两个概念:一是前缀,一是最长公共前缀blog
前缀的话就是每一个状态的最长串啦...固然要除去一些是由本来的节点剖分开的节点.这样的话一个节点仅仅只表示一个串.
排序
而最长公共后缀是十分好求的,咱们熟知parent边就是一个不断取后缀的过程,而后二者不断删去首字母,直到后缀相同的时候也就到了它们在parent树上的LCA,LCA节点的最长长度就是它们的最长公共后缀.get
因此咱们要算前缀的公共后缀就是考虑在一棵parent树上,考虑每一个LCA能够是多少个点对的LCA.[这应该是一个比较基础的树型DP...]string
那么其实只要知道LCA的全部儿子的right集合大小便可.(由于right集合的每一个位置分别表示了一段前缀[其实也就是这个节点下面的子树大小],)it
而后与它父亲的其它可能的前缀的可能相乘 就获得了有多少个点对.io
而后怎么求right集合的大小呢?
有一个性质就是parent树是一个right集合合并的过程,那么一个点的right集合大小就是其全部子树right集合大小之和.
还记得以前有画过一个图,如今再扯过来讲一下:
那么咱们要是能把叶子节点的值赋值为1,而后沿着parent往上走就能够获得了.
叶子节点怎么找呢?其实很简单...当你每次extend的时候,你会增长一个新的节点np,这个节点中就只包含一个元素{n},而后其它全部right集合中有n的都是它的祖先,因此它就是一个叶子节点,须要初始的时候就+1.
而有时候咱们将一个节点拆分红两个的时候,并无产生新的元素,因此不是叶子节点不须要+1.
固然有时候你不肯定的缘由是有的,好比:"abab"中的"ab"它明明有儿子,可是它确实又是一个叶子节点,提供了它本身right集合里面的2这个元素.若是把串最开始的部分加一个"无"的话,就能够看出来.
("ab")={2,4}--b-->("bab")={4}
--无-->("无ab")={2}
因此每一个前缀都必定在叶子节点上...好神奇啊...而后你们记住就行了.
固然还有一个树剖要知足的拓扑序的完成,这个就是按照长度来弄一下顺序就行了,越长的固然越在后面.具体的思路是一个联系桶排的思路,打过基数排序的同窗应该会很熟悉.
不懂的看代码就好.
#include<cstdio> #include<cstring> #include<algorithm>
using namespace std; typedef long long ll; const int maxn=1000010; int n,last=1,cnt=1; int a[maxn][26],mx[maxn],fa[maxn]; int T[maxn],Seq[maxn]; int f[maxn],w[maxn]; char ch[maxn]; void extend(int c){ int p=last,np=last=++cnt; mx[np]=mx[p]+1; f[np]=w[np]=1; while(!a[p][c] && p) a[p][c]=np,p=fa[p]; if(!p) fa[np]=1; else{ int q=a[p][c]; if(mx[q]==mx[p]+1) fa[np]=q; else{ int nq=++cnt;mx[nq]=mx[p]+1; memcpy(a[nq],a[q],sizeof(a[q])); fa[nq]=fa[q]; fa[q]=fa[np]=nq; while(a[p][c]==q) a[p][c]=nq,p=fa[p]; } } } void get_order(){ for(int i=1;i<=cnt;i++) T[mx[i]]++; for(int i=1;i<=n;i++) T[i]+=T[i-1]; for(int i=1;i<=cnt;i++) Seq[T[mx[i]]--]=i; } ll solve(){ ll ans=0; for(int i=cnt;i>=1;i--) f[fa[Seq[i]]]+=f[Seq[i]]; for(int i=cnt;i>=1;i--){ int x=Seq[i]; ans+=(ll)f[x]*w[fa[x]]*mx[fa[x]]; w[fa[x]]+=f[x]; } return ans; } int main(){ #ifndef ONLINE_JUDGE freopen("3238.in","r",stdin); freopen("3238.out","w",stdout); #endif scanf("%s",ch); n=strlen(ch); for(int i=n-1;i>=0;i--) extend(ch[i]-'a'); ll ans=(ll)(1+n)*n*(n-1)/2; get_order(); ans-=solve()*2; printf("%lld",ans); return 0; }