[ZJOI 2015]诸神眷顾的幻想乡
题意:给一棵树,每个节点有个值,问两点间叶子节点数\(?\)
思路:
\(SAM\)经典题目...
考虑每个节点的出度小于20,可以用\(Trie\)树,对于答案一定是树上的一个线段,统计一下扔到\(SAM\)就可以了...
#include <bits/stdc++.h>
using namespace std ;
#define ll long long
const int maxn = 2000010;
struct edge{
int nxt;
int to;
}e[maxn<<1];
ll ans;
int deg[maxn];
int w[maxn];
int edge_cnt;
int head[maxn];
int son[maxn][26];
int len[maxn];
int fail[maxn];
inline void Add_edge(int u,int v) {
e[++edge_cnt].to = v;
e[edge_cnt].nxt = head[u];
head[u] = edge_cnt;
return;
}
int tot = 1;
inline int extend(int p,int c) {
int np = ++tot;
len[np] = len[p] + 1;
while(!son[p][c] && p) {
son[p][c] = np;
p = fail[p];
}
if(!p) {
fail[np] = 1;
}
else {
int q = son[p][c];
if(len[p] + 1 == len[q]) {
fail[np] = q;
}
else {
int nq = ++tot;
len[nq] = len[p]+ 1;
memcpy(son[nq],son[q],sizeof(son[q]));
fail[nq] = fail[q];
fail[np] = fail[q] = nq;
while(son[p][c] == q) {
son[p][c] = nq;
p = fail[p];
}
}
}
return np;
}
inline void dfs(int x,int fa,int c) {
int tmp = extend(c,w[x]);
for(int i = head[x];i;i=e[i].nxt) {
int y = e[i].to;
if(y != fa) dfs(e[i].to,x,tmp);
}
}
int n,m;
int u,v;
int main () {
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 1;i <= n; ++i) {
cin >> w[i];
}
for(int i = 1;i < n; ++i) {
cin >> u >> v;
Add_edge(u,v);
Add_edge(v,u);
deg[u] ++;
deg[v] ++;
}
for(int i = 1;i <= n; ++i) {
if(deg[i] == 1) dfs(i,0,1);
}
for(int i = 1;i <= tot; ++i) {
ans += len[i] - len[fail[i]];
}
cout<<ans<<endl;
return 0;
}