博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

[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;
}
posted @ 2018-08-13 17:28  Allorkiya  阅读(105)  评论(0编辑  收藏  举报