bzoj3238:[Ahoi2013]差异
传送门
众所周知,两个后缀的最长公共前缀就是两个后缀在\(\rm sam\)上的\(\rm lca\)
那么对于这个题,我们发现那个式子其实就是\(\rm sam\)上的两点间的距离
我们就可以直接对于每条边算出贡献\((len_x-len_{fa_x})*size_x*(n-size_x)\)
然后就是答案了
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void read(int &x) {
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e6+10;
int n,m,size[maxn],tot,las,pre[maxn],cnt,nxt[maxn],h[maxn];char a[maxn];
long long ans;
struct sam{int link,len,ch[26];}s[maxn];
void sam_pre(){s[1].len=0,s[1].link=-1;tot=las=1;}
void ins(int x){
int cur=++tot,p=las;s[cur].len=s[las].len+1;size[tot]=1;
while(p!=-1&&!s[p].ch[x])s[p].ch[x]=cur,p=s[p].link;
if(p==-1)s[cur].link=1;
else {
int q=s[p].ch[x];
if(s[q].len==s[p].len+1)s[cur].link=q;
else {
int now=++tot;s[now].len=s[p].len+1;
s[now].link=s[q].link;
memcpy(s[now].ch,s[q].ch,sizeof s[q].ch);
while(p!=-1&&s[p].ch[x]==q)s[p].ch[x]=now,p=s[p].link;
s[cur].link=s[q].link=now;
}
}
las=cur;
}
void add(int x,int y){pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;}
void dfs(int x){
for(rg int i=h[x];i;i=nxt[i]){
dfs(pre[i]),size[x]+=size[pre[i]];
ans+=1ll*(s[pre[i]].len-s[x].len)*size[pre[i]]*(n-size[pre[i]]);
}
}
int main(){
scanf("%s",a+1),n=strlen(a+1),sam_pre();
for(rg int i=1;i<=n;i++)ins(a[i]-'a');
for(rg int i=2;i<=tot;i++)add(s[i].link,i);
dfs(1),printf("%lld\n",ans);
}