考虑SAM是tire的集合物。
所以sam的父节点都是一个串的后缀。
所以我们反过来,再求\(LCA\)就是LCP了。
这个求和类似于一个路径求和。
我们考虑每条边的权值为\(len_p - len_{link_p}\)
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 500005
ll link[N << 1],ch[N << 1][30],f[N << 1],g[N << 1],len[N << 1];
ll siz[N << 1];
char a[N];
ll nod = 1,lst = 1;
inline void insert(int c){
int p = lst,q = ++nod;lst = q;
len[q] = len[p] + 1;
siz[q] = 1;
while(!ch[p][c] && p != 0){//向上找
ch[p][c] = q;
p = link[p];
}
if(p == 0)
link[q] = 1;
else{
int x = ch[p][c];
if(len[p] + 1 == len[x]){
link[q] = x;
}else{
int y = ++ nod ;//复制一个新节点
link[y] = link[x];
link[x] = link[q] = y;
len[y] = len[p] + 1;
std::memcpy(ch[y],ch[x],sizeof(ch[x]));
while(p != 0 && ch[p][c] == x){
ch[p][c] = y;
p = link[p];
}
}
}
}
ll n,ans;
struct P{
int to,next;
}e[N << 2];
ll cnt,head[N << 1];
inline void add(int x,int y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
inline void dfs(ll u,ll f){
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == f)continue;
dfs(v,u);
siz[u] += siz[v];
}
if(u != 1)
ans = ans + (len[u] - len[link[u]]) * (siz[u]) * (n - siz[u]);
}
int main(){
scanf("%s",a + 1);
n = std::strlen(a + 1);
for(int i = n;i >= 1;--i)
insert(a[i] - 'a');
for(int i = 1;i <= nod;++i)
add(link[i],i);
dfs(1,0);
std::cout<<ans<<std::endl;
}