这个题有一个比较提示性的条件:叶子节点只有20个,点数总共是100000,那显然是要100000*20级别的效率
所以就考虑对叶子节点进行处理,首先要把树上的链拆成子串,所以就不会了。。
可以试图利用叶子节点后面没有点的性质来确定区间左端点,但右端点需要到其他的叶子节点
所以就暴力加点,,记录当前扩展点,,类似可持久化结构。。
然后扔进广义后缀自动机,扫一遍点就可以了
码:
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define N 100005
vector<int>v[N*20*2],yz;
int ch[N*20*2][11],n,m,i,j,a[N],x,y,p,l[N*20*2],tot,fu[N*20*2],laji;
long long ans;
int jia(int o,int last)
{
if(ch[last][o]!=0&&l[ch[last][o]]==l[last]+1){last=ch[last][o];return last; }
int i,np=++tot,j;
p=last;last=np;l[np]=l[p]+1;
for(;ch[p][o]==0&&p!=0;p=fu[p])ch[p][o]=np;
if(p==0)fu[np]=1;
else
{
int q=ch[p][o];
if(l[q]==l[p]+1)fu[np]=q;
else
{
int nq=++tot;l[nq]=l[p]+1;
for(j=0;j<=10;j++)ch[nq][j]=ch[q][j];
fu[nq]=fu[q];fu[q]=fu[np]=nq;
for(;ch[p][o]==q;p=fu[p])ch[p][o]=nq;
}
}
return np;
}
void calc(int o,int fa)
{int i;
ans+=1ll*(l[o]-l[fa]);
for(i=0;i<v[o].size();i++)
calc(v[o][i],o);
}
void dfs(int o,int fa)
{
int i;
if(v[o].size()==1)
{
yz.push_back(o);
}
for(i=0;i<v[o].size();i++)
{
int nd=v[o][i];
if(nd==fa)continue;
dfs(nd,o);
}
}
void dfs2(int o,int fa,int t)
{
int i,lin;
lin=jia(a[o],t);
for(i=0;i<v[o].size();i++)
{
int nd=v[o][i];
if(nd==fa)continue;
dfs2(nd,o,lin);
}
}
int main()
{
scanf("%d%d",&n,&laji);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}tot=1;
dfs(1,0);
for(i=0;i<yz.size();i++)
{
int nd=yz[i];
dfs2(nd,0,1);
}
for(i=0;i<=tot;i++)v[i].clear();
for(i=2;i<=tot;i++)
{
v[fu[i]].push_back(i);
}
calc(1,0);
printf("%lld",ans);
}