P4248 [AHOI2013]差异 SAM
题意:
分析:
我们观察题面发现这个式子长得很像树上两点间的距离,这就让我们联想到建一颗 \(SAM\),然后构建出 \(parent\) 树,原式等价于求 \(parent\) 树上任意两点间距离的和值
典型的树上 \(dp\),我们考虑每一条边的贡献,每一条边对原式的贡献等价于 \(经过该边的点对的数目\times 边的长度\)
所以我们可以推出 \(ans=\sum (len[i]-len[link[i]])*(n-siz[i])*siz[i]\)
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 1e6+5;
int n;
long long ans=0;
char ch[maxn];
struct suffix_automaton
{
int cnt,lst;
int link[maxn],len[maxn],trans[maxn][26],t[maxn],sa[maxn],siz[maxn];
suffix_automaton(){cnt=lst=1;}
void insert(int x)
{
int cur=++cnt,tmp=lst;lst=cnt;
len[cur]=len[tmp]+1;siz[cur]=1;
for(;tmp&&!trans[tmp][x];tmp=link[tmp]) trans[tmp][x]=cur;
if(!tmp)
{
link[cur]=1;
}
else
{
int q=trans[tmp][x];
if(len[tmp]+1==len[q])
{
link[cur]=q;
}
else
{
int clone=++cnt;
len[clone]=len[tmp]+1;
link[clone]=link[q];
link[q]=link[cur]=clone;
for(int i=0;i<26;i++) trans[clone][i]=trans[q][i];
for(;tmp&&trans[tmp][x]==q;tmp=link[tmp]) trans[tmp][x]=clone;
}
}
}
void sort()
{
for(int i=1;i<=cnt;i++) t[len[i]]++;
for(int i=1;i<=cnt;i++) t[i]+=t[i-1];
for(int i=1;i<=cnt;i++) sa[t[len[i]]--]=i;
for(int i=cnt;i>=1;i--)
{
int x=sa[i];siz[link[x]]+=siz[x];
ans+=1ll*(n-siz[x])*siz[x]*(len[x]-len[link[x]]);
}
}
}sam;
void work()
{
scanf("%s",ch+1);n=strlen(ch+1);
for(int i=n;i>=1;i--) sam.insert(ch[i]-'a');
sam.sort();
printf("%lld\n",ans);
}
}
int main()
{
zzc::work();
return 0;
}