LGP3346-[ZJOI2015]诸神眷顾的幻想乡 (GSAM)
LGP3346-[ZJOI2015]诸神眷顾的幻想乡
题意
给定一个\(n\)个节点的树,每个节点有一个权值,保证树的叶子节点不超过\(20\)个,求树上本质不同的路径有多少条。
Sol
从叶子节点为根开始建树,建出树上的广义后缀自动机后,\(\Sigma node[i].len - node[node[i].fa].len\)就是答案。
本质上树上所有路径都会被包含在所有叶子节点为根的树的路径中。
Code(在线)
/*
* @Author: quun
* @Date: 2021-09-24 22:28:36
* @Last Modified by: quun
* @Last Modified time: 2021-09-24 22:50:16
*/
#include<bits/stdc++.h>
using namespace std;
int last=1,tot=1;
const int N = 1e6+10;
typedef long long ll;
/**
* GSAM + 在线正版
*
* 求树上本质不同的路径个数
*
*/
struct Node{
int len,fa;
int ch[10];
}node[N*2];
char s[N];
int n,c;
int idx;
int h[N*2],e[N*3],ne[N*3];
void add(int x,int y){
e[++idx] = y ,ne[idx] = h[x] ,h[x] = idx;
}
int pos[N];
int val[N];
int ind[N];
int extend(int c,int last){
if(node[last].ch[c]){
int p = last,x = node[p].ch[c];
if(node[p].len + 1 == node[x].len){//特判1
return x;
}
else{//特判2
int y = ++tot;node[y].len = node[p].len+1;
for(int i=0;i<=9;++i)node[y].ch[i] = node[x].ch[i];
while(p&&node[p].ch[c]==x)node[p].ch[c]=y,p=node[p].fa;
node[y].fa=node[x].fa,node[x].fa=y;
return y;
}
}
int p = last ,np = last = ++tot;
node[np].len = node[p].len + 1;
for(;p && !node[p].ch[c]; p = node[p].fa){
node[p].ch[c] = np;
}
if(!p)node[np].fa = 1;
else{
int q = node[p].ch[c];
if(node[q].len == node[p].len + 1){
node[np].fa = q;
}
else{
int nq = ++tot;
node[nq] = node[q];
node[nq].len = node[p].len+1;
node[q].fa = node[np].fa = nq;
for(;p && node[p].ch[c] == q;p = node[p].fa){
node[p].ch[c] = nq;
}
}
}
return last;
}
void dfs(int x,int fa,int dep){
pos[x] = extend(val[x],pos[fa]);
for(int i = h[x]; i ;i = ne[i]){
int to = e[i];
if(to == fa)continue;
dfs(to,x,dep+1);
}
}
int main(){
scanf("%d %d",&n,&c);
for(int i = 1;i <= n;++i){
scanf("%d",&val[i]);
}
for(int i = 1;i <= n-1;++i){
int l,r;
scanf("%d %d",&l,&r);
add(l,r);
add(r,l);
ind[l]++,ind[r]++;
}
pos[0] = 1;
for(int i=1;i<=n;++i){
if(ind[i] == 1){
dfs(i,0,1);
}
}
ll ans = 0;
for(int i=2;i<=tot;++i){
ans = ans + node[i].len - node[node[i].fa].len;
}
printf("%lld\n",ans);
return 0;
}