Description
定义lcp(a,b)为字符串a、b的最长公共前缀,len(a)为字符串a的长度。对于长度位n的字符串S定义Ti为S从第i位开始的后缀,求
∑1<=i<j<=nlen(Ti)+len(Tj)−len(lcp(Ti,Tj))
∑
1
<=
i
<
j
<=
n
l
e
n
(
T
i
)
+
l
e
n
(
T
j
)
−
l
e
n
(
l
c
p
(
T
i
,
T
j
)
)
Solution
前面两项挺好求的,问题是后面的lcp要怎么搞
由于我太弱了不会后缀数组,因此考虑把S倒过来建SAM,得出的parent树上两节点的lca即为两字符串的lcp(反向前缀树),树形dp统计每一个节点单独的贡献即可
记得开LL
Code
#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define copy(x,t) memcpy(x,t,sizeof(x))
typedef long long LL;
const int N=1000205;
const int E=1000205;
const int L=500005;
struct edge {int x,y,next;} e[E];
int ls[N],edCnt;
int size[N];
int rec[N][26],len[N],fa[N];
int last,cnt;
LL ans;
char str[L];
void addEdge(int x,int y) {
e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
}
void insert(int ch) {
int p,q,np,nq;
p=last; last=np=++cnt; len[np]=len[p]+1;
size[np]=1;
while (p&&!rec[p][ch]) {
rec[p][ch]=np;
p=fa[p];
}
if (!p) fa[np]=1;
else {
q=rec[p][ch];
if (len[p]+1==len[q]) {
fa[np]=q;
} else {
nq=++cnt; len[nq]=len[p]+1;
copy(rec[nq],rec[q]);
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
while (p&&rec[p][ch]==q) {
rec[p][ch]=nq;
p=fa[p];
}
}
}
}
void dfs(int now) {
for (int i=ls[now];i;i=e[i].next) {
dfs(e[i].y);
size[now]+=size[e[i].y];
}
if (now==1) return ;
len[now]-=len[fa[now]];
ans-=(LL)size[now]*(LL)(size[now]-1)*(LL)len[now];
}
int main(void) {
scanf("%s",str);
int len=strlen(str);
cnt=last=1;
drp(i,len-1,0) {
insert(str[i]-'a');
}
rep(i,2,cnt) addEdge(fa[i],i);
ans=(LL)(len-1)*(LL)(len+1)*(LL)len/2;
dfs(1);
printf("%lld\n", ans);
return 0;
}