P3346 [ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)

诸神眷顾的幻想乡

给出一棵树

叶子节点<=20

询问树上一共可能有多少个不同颜色序列

颜色值域<=10

这题有一个结论

从树的所有叶子节点为根开始搜,搜到的从根到节点的路径

包含树上所有叶子的路径

观察到叶子节点的数量<=20

那么就可以以每个叶子节点为根搜索,把搜到的路径

依次插入同一颗字典树内

然后对这个字典树建GSAM

最后套板子即可

 
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e6+100;
vector<int> g[maxn];
int ch[maxn][12],tol,tot=1;
int len[maxn],link[maxn],nxt[maxn][12];
int ed[maxn];
int a[maxn];
int n,c;
void dfs (int u,int f,int ff) {
	//在树上搜,同时把当前字符插入字典树
	if (!ch[ff][a[u]]) ch[ff][a[u]]=++tol;
	for (int v:g[u]) {
		if (v==f) continue;
		dfs(v,u,ch[ff][a[u]]);
	}
}
int sam_extend (int c,int lst) {
	int cur=++tot;
	len[cur]=len[lst]+1;
	int p=lst;
	while (p&&!nxt[p][c]) {
		nxt[p][c]=cur;
		p=link[p];
	}
	if (!p) {
		link[cur]=1;
	}
	else {
		int q=nxt[p][c];
		if (len[p]+1==len[q]) {
			link[cur]=q;
		}
		else {
			int clone=++tot;
			len[clone]=len[p]+1;
			for (int i=0;i<12;i++) {
				nxt[clone][i]=nxt[q][i];
			}
			link[clone]=link[q];
			while (p&&nxt[p][c]==q) {
				nxt[p][c]=clone;
				p=link[p];
			}
			link[q]=link[cur]=clone;
		}
	}
	return cur;
}
void GSA (int u,int f,char lc) {
	if (u) {
		ed[u]=sam_extend(lc,ed[f]);
	} 
	for (int i=0;i<12;i++) {
		if (ch[u][i]) {
			GSA(ch[u][i],u,i);
		}
	}
}
int main () {
	scanf("%d%d",&n,&c);
	for (int i=1;i<=n;i++) scanf("%d",a+i);
	for (int i=1;i<n;i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	for (int i=1;i<=n;i++) {
		if (g[i].size()>1) continue;
		dfs(i,0,0);
	}
	ed[0]=1;
	GSA(0,-1,0);
	long long ans=0;
	for (int i=2;i<=tot;i++) ans+=len[i]-len[link[i]];
	printf("%lld\n",ans);
	
}
posted @ 2021-07-15 23:44  zlc0405  阅读(35)  评论(0编辑  收藏  举报