[AHOI2013]差异 【SAM后缀树+树形dp】

[AHOI2013]差异

万能的后缀树啊2333

建出后缀树,一个节点的祖先节点都是他的后缀,一个节点的儿子节点都是以他为后缀的串。所以两个串的lcs就是他们的lca节点的 l e n len len

这道题可以对反串建后缀树,树边 ( u , v ) (u,v) (u,v) 权值为 ∣ l e n u − l e m v ∣ |len_u-lem_v| lenulemv ,答案就是所有关键点之间的路径权值之和。

#include<bits/stdc++.h>
#define N 1000006
using namespace std;
typedef long long ll;
char s[N];
int lst,cnt,link[N];
ll n,ans,len[N],siz[N];
vector<int> son[N];
struct SAM{
	map<char,int> trans[N];
	void insert(char c){
		int z=++cnt,p=lst; siz[z]=1,len[z]=len[lst]+1;
		while(p!=-1&&!trans[p].count(c))trans[p][c]=z,p=link[p];
		if(p==-1) link[z]=0;
		else{
			int q=trans[p][c];
			if(len[q]==len[p]+1) link[z]=q; 
			else{
				int clone=++cnt; len[clone]=len[p]+1;
				trans[clone]=trans[q];
				while(p!=-1&&trans[p][c]==q) trans[p][c]=clone,p=link[p];
				link[clone]=link[q],link[z]=link[q]=clone;
			}
		}
		lst=z;
	}
	void dp(int x=0){
		int y;
		for(int i=0;i<son[x].size();i++) y=son[x][i],dp(y),siz[x]+=siz[y];
		if(x) ans+=siz[x]*(n-siz[x])*(len[x]-len[link[x]]); 
	}
}sam;
int main(){
	scanf("%s",s+1); n=strlen(s+1);
	link[0]=-1;
	for(int i=n;i>=1;i--) sam.insert(s[i]);
	for(int i=1;i<=cnt;i++) son[link[i]].push_back(i);
	sam.dp();
	cout<<ans;
} 
posted @ 2022-10-10 20:18  缙云山车神  阅读(16)  评论(0编辑  收藏  举报