正题
题目链接:https://www.luogu.com.cn/problem/P4248
题目大意
TiT_iTi表示后缀i∼ni\sim ni∼n
一个字符串求
∑i=1n∑j=inlen(Ti)+len(Tj)−2∗lcp(Ti,Tj)\sum_{i=1}^n\sum_{j=i}^nlen(T_i)+len(T_j)-2*lcp(T_i,T_j)i=1∑nj=i∑nlen(Ti)+len(Tj)−2∗lcp(Ti,Tj)
解题思路
有两种做法,这里标程用的是SAMSAMSAM
SAMSAMSAM做法
这个式子和树上路径长度很像,顺着这个思路,我们可以想到SAMSAMSAM上的ParentParentParent树。
两个后缀的lcplcplcp就是ParentParentParent树上的LCALCALCA的节点代表的字符串,我们让边长为lenx−lenfailxlen_x-len_{fail_x}lenx−lenfailx,然后答案就变为了树上的每条路径长度和。
那一条边的贡献就是(num−sizx)∗sizx∗len(num-siz_x)*siz_x*len(num−sizx)∗sizx∗len‘
统计即可,时间复杂度O(n)O(n)O(n)
SASASA做法
我们先计算定值∑i=1n∑j=inlen(Ti)+len(Tj)\sum_{i=1}^n\sum_{j=i}^nlen(T_i)+len(T_j)∑i=1n∑j=inlen(Ti)+len(Tj)
这里的定值就是n∗(n−1)∗(n+1)2\frac{n*(n-1)*(n+1)}{2}2n∗(n−1)∗(n+1)
然后我们考虑如何计算LCPLCPLCP的和
heighti=LCP(i,i−1)height_i=LCP(i,i-1)heighti=LCP(i,i−1)
我们有LCP(i,j)=min{heightk}(i<k≤j)LCP(i,j)=min\{height_k\}(i<k\leq j)LCP(i,j)=min{heightk}(i<k≤j)
所以这里的答案就变为了∑i=2n∑j=inmin{heightk}(i≤k≤j)\sum_{i=2}^{n}\sum_{j=i}^nmin\{height_k\}(i\leq k\leq j)i=2∑nj=i∑nmin{heightk}(i≤k≤j)
我们每个heightiheight_iheighti的贡献分开统计即可
codecodecode
#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);
}