1. 程式人生 > 其它 >[AHOI2013]差異

[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;
}