专栏文章

题解:P5043 【模板】树同构([BJOI2015]树的同构)

P5043题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mioalpig
此快照首次捕获于
2025/12/02 16:04
3 个月前
此快照最后确认于
2025/12/02 16:04
3 个月前
查看原文
首先简单讲解一下树哈希。

树哈希

树哈希和别的哈希没有本质的区别,只不过映射对象是树。一般我们的哈希函数如此设计。
f(x)=(c+yxsong(y))modmf(x) = (c + \sum_{y \in xson} g(y)) \mod m
实际操作时,首项常数通常取一,模数采取自然溢出。
这里有模板题以及参考代码。
CPP
const ull mask = 1145141919810;
//mask取随机数即可
inline ull g(ull x) {
	x ^= mask;
	x ^= x << 13;
	x ^= x >> 7;
	x ^= x << 17;
	x ^= mask;
  // g函数通常写法
	return x;
}
void dfs(int s, int fa) {
	Hash[s] = 1ull;//c取1
	for(int to: e[s]) {
		if(to == fa) continue;
		dfs(to, s);
		Hash[s] += g(Hash[to]);
	}
}
必要的前置知识讲完了,那我们回到题面。
在两棵同构的无根树上,我们思考什么是在编号改变后始终不变的。
没错就是重心的位置。而树的重心最多只有两个。于是我们只需要找出树的两个重心,记录以两个重心为根无根树的哈希值比较即可。
CPP
#include <bits/stdc++.h>
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
typedef unsigned long long ull;
using namespace std;
constexpr int N = 55;
const ull mask = mt19937_64(time(0))();
inline ull g(ull x) {
	x ^= mask;
	x ^= x << 13;
	x ^= x >> 7;
	x ^= x << 17;
	x ^= mask;
	return x;
}
long long target, g1, g2, m, n, root, p, dp[N], dep[N], siz[N];
vector<int> e[N];
void init(int s, int fa) {
	dep[s] = dep[fa] + 1;
	dp[root] += dep[s];
	siz[s] = 1;
	for(register int to : e[s]) {
		if(to == fa) continue;
		init(to, s);
		siz[s] += siz[to];
	}
}
void dfs(int s, int fa) {
	for(register int to : e[s]) {
		if(to == fa) continue;
		dp[to] = dp[s] + n - siz[to] * 2;
		dfs(to, s);
	}
}
ull Hash(int s, int fa) {
	ull hash = 1ull;
	for(register int to : e[s]) {
		if(to == fa) continue;
		hash += g(Hash(to, s));
	}
	return hash;
}
map<pair<ull, ull>, int>mp;
int main() {
	ios :: sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin >> m;
	rep(i, 1, m) {
		cin >> n;
		g1 = g2 = 0;
		memset(dp, 0, sizeof dp);
		memset(dep, 0, sizeof dep);
		memset(siz, 0, sizeof siz);
		target = 1e18;
		rep(j, 1, 50) e[j].clear();
		rep(j, 1, n) {
			cin >> p;
			if(!p) {
				root = j;
				continue;
			}
			e[p].push_back(j);
			e[j].push_back(p);
		}
		init(root, 0);
		dfs(root, 0);
		rep(j, 1, n) {
			target = min(target, dp[j]);
		}
		rep(j, 1, n) {
			if(dp[j] == target) {
				if(g1) {
					g2 = j;
				}
				else {
					g1 = j;
				}
			}
		}
      //预处理出树的重心
		ull p1 = Hash(g1, 0);
		ull p2 = Hash(g2, 0);
		if(p1 > p2) {
			swap(p1, p2);
		}
		auto now = make_pair(p1, p2);
    //记录哈希值,存储进map即可
		if(mp[now]) {
			cout << mp[now] << '\n';
		}
		else {
			cout << i << '\n';
			mp[now] = i;
		}
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...