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