3238: [Ahoi2013]差异
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1968 Solved: 896
[ Submit][ Status][ Discuss]
Description
Input
一行,一个字符串S
Output
一行,一个整数,表示所求值
Sample Input
cacao
Sample Output
54
HINT
2<=N<=500000,S由小写英文字母组成
Source
感觉窝会求后缀数组然后完全不会用……
好伐我还是写了SAM2333333。
显然题意只要我们求出∑LCP(i,j)(i!=j)就好
首先我们跑一遍SAM求出后缀树,然后拓扑一下就能求出所有right集合的大小。
显然两个后缀的最长公共前缀长就是他们在后缀树上的lca的深度。
我们可以考虑对每个深度进行分段,即点x对答案的贡献只包含其在后缀树上的父节点的深度以上且在自身深度以下的答案。
显然这样做的答案就是∑deep[i]*∑right[i]-right[son[i]]
又因为right[i]=∑right[son[i]]
那么每个点的贡献就是deep[i]*right[i]*right[i-1]了
#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
const int N=1000005,A=26;
struct SAM{
char s[N];int t,l,q[N],last,a[N];
int fail[N],e[N][A],v[N],d[N];
SAM(){t=last=1;}
void extend(int c){
int np=++t,p=last;
d[np]=d[p]+1,v[last=np]=1;
for(;!e[p][c]&&p;p=fail[p])
e[p][c]=np;
if(!p)fail[np]=1;
else {
int q=e[p][c],nq;
if(d[q]!=d[p]+1){
nq=++t;d[nq]=d[p]+1;
memcpy(e[nq],e[q],sizeof(int[A]));
fail[nq]=fail[q];fail[np]=fail[q]=nq;
for(;e[p][c]==q;p=fail[p])e[p][c]=nq;
} else fail[np]=q;
}
}
void read(){scanf("%s",s);l=strlen(s);}
void build(){for(int i=0;i<l;i++)extend(s[i]-'a');}
ll getans(){
for(int i=1;i<=t;i++)a[d[i]]++;
for(int i=1;i<=l;i++)a[i]+=a[i-1];
for(int i=t;i;i--)q[a[d[i]]--]=i;
for(int i=t;i;i--)v[fail[q[i]]]+=v[q[i]];
v[1]=v[0]=0;ll ret=(ll)(l-1)*l*(l+1)/2;
for(int i=2;i<=t;i++)ret-=(ll)v[i]*(v[i]-1)*(d[i]-d[fail[i]]);
return ret;
}
} sam;
int main(){
sam.read();sam.build();
printf("%lld\n",sam.getans());
return 0;
}