[AHOI2013]差异

考虑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;
}
posted @ 2021-08-05 21:41  fhq_treap  阅读(38)  评论(0编辑  收藏  举报