正题
题目链接:https://www.luogu.com.cn/problem/P4248
题目大意
表示后缀
一个字符串求
解题思路
有两种做法,这里标程用的是
做法
这个式子和树上路径长度很像,顺着这个思路,我们可以想到 上的 树。
两个后缀的 就是 树上的 的节点代表的字符串,我们让边长为 ,然后答案就变为了树上的每条路径长度和。
那一条边的贡献就是
‘
统计即可,时间复杂度
做法
我们先计算定值
这里的定值就是
然后我们考虑如何计算
的和
我们有
所以这里的答案就变为了
我们每个
的贡献分开统计即可
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2e6+10;
struct node{
ll to,next,w;
}a[N];
ll n,tot,ls[N],ans,num,siz[N];
ll next[N][26],len[N],fail[N],cnt;
char s[N];
void New_Point(ll x,ll v){
next[x][v]=++cnt;
len[cnt]=len[x]+1;
}
void addl(ll x,ll y,ll w){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;
a[tot].w=w;
}
void Make_SAM(){
ll now;
now=cnt=1;
for(ll i=1;i<=n;i++){
ll val=s[i]-'a';
New_Point(now,val);
ll x=now,y;now=cnt;
for(y=fail[x];y;y=fail[y])
if(!next[y][val])
next[y][val]=now;
else{
if(len[y]+1==len[next[y][val]])
fail[now]=next[y][val];
else{
ll z=next[y][val];
New_Point(y,val);
fail[cnt]=fail[z];
fail[z]=fail[now]=cnt;
for(ll i=0;i<26;i++)
next[cnt][i]=next[z][i];
for(ll j=y;j;j=fail[j])
if(next[j][val]==z)
next[j][val]=cnt;
}
break;
}
siz[now]=1;
if(!y) fail[now]=1;
}
for(ll i=2;i<=cnt;i++){
num+=siz[i];
addl(fail[i],i,len[i]-len[fail[i]]);
}
}
void dfs(ll x){
for(ll i=ls[x];i;i=a[i].next){
ll y=a[i].to;dfs(y);
ans+=siz[y]*(num-siz[y])*a[i].w;
siz[x]+=siz[y];
}
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
Make_SAM();
dfs(1);
printf("%lld",ans);
}