Description
幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日。
粉丝们非常热情,自发组织表演了一系列节目给幽香看。幽香当然也非常高兴啦。
这时幽香发现了一件非常有趣的事情,太阳花田有n块空地。在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来。也就是说,这n块空地形成了一个树的结构。
有n个粉丝们来到了太阳花田上。为了表达对幽香生日的祝贺,他们选择了c中颜色的衣服,每种颜色恰好可以用一个0到c-1之间的整数来表示。并且每个人都站在一个空地上,每个空地上也只有一个人。这样整个太阳花田就花花绿绿了。幽香看到了,感觉也非常开心。
粉丝们策划的一个节目是这样的,选中两个粉丝A和B(A和B可以相同),然后A所在的空地到B所在的空地的路径上的粉丝依次跳起来(包括端点),幽香就能看到一个长度为A到B之间路径上的所有粉丝的数目(包括A和B)的颜色序列。一开始大家打算让人一两个粉丝(注意:A,B和B,A是不同的,他们形成的序列刚好相反,比如红绿蓝和蓝绿红)都来一次,但是有人指出这样可能会出现一些一模一样的颜色序列,会导致审美疲劳。
于是他们想要问题,在这个树上,一共有多少可能的不同的颜色序列(子串)幽香可以看到呢?
太阳花田的结构比较特殊,只与一个空地相邻的空地数量不超过20个。
题解:
直接对于每个叶子结点遍历整棵树,建出广义后缀自动机就好了,这样就能够保证得出来的字符集就是题目求的字符集,然后知道了一个小知识:原来sam是可以用trie的方法来建的……
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,c,col[Maxn],d[Maxn];
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y){int t=++len;e[t].y=y;e[t].next=last[x];last[x]=t;}
bool vis[Maxn];
int Last,tot=1;
int son[Maxn*40][10],mx[Maxn*40],par[Maxn*40];
LL ans=0;
void extend(int x)
{
int p=Last,np=++tot;mx[np]=mx[p]+1;
while(p&&!son[p][x])son[p][x]=np,p=par[p];
if(!p)par[np]=1;
else
{
int q=son[p][x];
if(mx[p]+1==mx[q])par[np]=q;
else
{
int nq=++tot;mx[nq]=mx[p]+1;
for(int i=0;i<10;i++)son[nq][i]=son[q][i];
par[nq]=par[q];
par[q]=par[np]=nq;
while(son[p][x]==q)son[p][x]=nq,p=par[p];
}
}Last=np;
ans+=(LL)(mx[np]-mx[par[np]]);
}
void dfs(int x)
{
vis[x]=true;
extend(col[x]);int t=Last;
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(vis[y])continue;
Last=t;dfs(y);
}
}
int main()
{
n=read(),c=read();
for(int i=1;i<=n;i++)col[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
ins(x,y),ins(y,x);d[x]++,d[y]++;
}
for(int i=1;i<=n;i++)
if(d[i]==1)
{
memset(vis,false,sizeof(vis));
Last=1;
dfs(i);
}
printf("%lld",ans);
}