「JSOI2016」独特的树叶(树Hash)

opening words

题目链接

这是我在学玩 树hash 后来做的一道题,似乎 树hash 的题很少,我只找到了这一道,但是却是道省选题,说明省选还是可能出的。

problem outline

给定两棵树,其中一棵为 \(n\) 个节点,另一棵树是在上一棵树的基础上新增了一个叶子节点构成的(一共有 \(n+1\) 个节点),请你找出新增的是哪个叶子节点,输出编号最小的那一个。 (\(n <= 10^5\)

problem solution

这道题显然是个树同构的题目吧,所以需要用到 树hash。

因为是无根树,所以我们先利用换根DP求出第一棵树的每个节点做根时的Hash值,用 map 存起来。

然后对于第二棵树做同样的操作。

我们发现叶子节点的度数一定是 1,所以我们可以找到哪些是叶子节点,我们发现去掉叶子节点 \(u\) 的整棵树的hash值就是 \(ha[fa[u]] - base*pri[1]\)

然后我们就可以在 map 里面找是否可以匹配。

problem std

// by longdie 
#include <bits/stdc++.h> 
#define ull unsigned long long 
using namespace std; 
const int N = 1e5 + 5;  
const ull base = 1; 
map<ull, int> p; 
int n, vis[N*20], tot, pri[N]; 
struct Tree {
	ull f[N], ha[N]; 
	int n, head[N], cnt, pa[N], d[N], siz[N]; 
	struct edge { int to, next; } e[N<<1]; 
	void add(int x, int y) {
		e[++cnt] = (edge){y, head[x]}, head[x] = cnt, d[x]++; 
	}
	void dfs0(int u, int fa) {
		siz[u] = 1, ha[u] = base, pa[u] = fa; 
		for(register int i = head[u], v; i; i = e[i].next) {
			v = e[i].to; 
			if(v == fa) continue; 
			dfs0(v, u); 
			siz[u] += siz[v]; 
			ha[u] += ha[v] * pri[siz[v]]; 
		}
	}
	void dfs1(int u, int fa) {
		if(u != 1) {
			f[u] = ha[u] + (f[fa] - ha[u]*pri[siz[u]])*pri[n-siz[u]]; 
		}
		for(register int i = head[u], v; i; i = e[i].next) {
			v = e[i].to; 
			if(v == fa) continue; 
			dfs1(v, u); 
		}
	}
	void solve(int m) {
		n = m; 
		for(register int i = 1, x, y; i < n; ++i) {
			scanf("%d%d", &x, &y), add(x, y), add(y, x); 
		}
		dfs0(1, 0); 
		f[1] = ha[1]; 
		dfs1(1, 0); 
	} 
} a, b; 
signed main() {
	scanf("%d", &n);
	for(register int i = 2; i <= 2e6 && tot <= n+1; ++i) {
		if(!vis[i]) pri[++tot] = i; 
		for(register int j = 1; j <= tot && pri[j]*i <= 2e6; ++j) {
			vis[i * pri[j]] = 1; 
			if(i % pri[j] == 0) break; 
		}
	}  
	a.solve(n);
	for(register int i = 1; i <= n; ++i) {
		p[a.f[i]] = 1; 
	}
	b.solve(n + 1); 
	for(register int i = 1; i <= n + 1; ++i) {
		if(b.d[i] != 1) continue; 
		int u = b.e[b.head[i]].to; 
		ull tmp = b.f[u] - 2; 
		if(p.find(tmp) != p.end()) return printf("%d\n", i), 0; 
	}	
	return 0; 
}
posted @ 2021-03-10 21:02  longdie  阅读(96)  评论(0编辑  收藏  举报