题意
给出一棵树,求这棵树上有多少不同的子串。
n<=100000,c<=10
分析
考虑到叶节点数量不超过20个,有一个神奇的结论就是每个串必然是以某个叶节点为根的树上的一条链。那么就以每棵树为根建一棵后缀自动机,并把所有后缀自动机合并成一个,然后直接统计答案即可。
真的觉得后缀自动机好强大啊!!!
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 100005
#define LL long long
using namespace std;
int cnt,n,l[N*20],last[N],fa[N*20],ch[N*20][15],color[N],d[N],c;
struct edge{int to,next;}e[N*2];
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
int ins(int p,int x)
{
int np,q,nq;
np=++cnt;l[np]=l[p]+1;
for (;!ch[p][x]&&p;p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=1;
else
{
q=ch[p][x];
if (l[q]==l[p]+1) fa[np]=q;
else
{
nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
return np;
}
void dfs(int x,int fa,int p)
{
int t=ins(p,color[x]);
for (int i=last[x];i;i=e[i].next)
if (e[i].to!=fa) dfs(e[i].to,x,t);
}
int main()
{
scanf("%d%d",&n,&c);
for (int i=1;i<=n;i++) scanf("%d",&color[i]);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
d[x]++;d[y]++;
}
cnt=1;
for (int i=1;i<=n;i++)
if (d[i]==1) dfs(i,0,1);
LL ans=0;
for (int i=1;i<=cnt;i++)
ans+=l[i]-l[fa[i]];
printf("%lld",ans);
return 0;
}