TREEISO - Tree Isomorphism

如果是有根树,我们只需要从根开始树哈希即可。如果是无根树,一种方案是,依次选取每个点作为根,但复杂度无法接受。当然可以换根 DP,但有一个比较聪明的方法。

找到两棵树的重心进行树哈希。注意重心最多有两个,所以要进行两次树哈希,对哈希值进行对比。

fif_i 为以 ii 为根的子树的哈希值。我们通常可以设计哈希转移:fi=1+jsonuF(fj)f_i = 1 +\sum \limits_{j \in son_u} F(f_j)。其中FF 是哈希函数。哈希函数的具体方法很多,例如 F(i)=114×iF(i)=114 \times i。但是由于哈希本身并不能保证 100%100\% 的正确性,所以哈希函数的设计会很大程度上导致哈希的结果。例如上述的 F(i)=114×iF(i) = 114 \times i 并不能通过此题。

通常我们使用 Xor-Hash,代码实现如下:

ull shift(ull x)
{
	x ^= P;
	x ^= (x << 7);
	x ^= (x >> 13);
	x ^= (x << 17);
	x ^= P;
	return x;
}

其中 PP 是一个常数,可以通过随机获得,也可以自己设定。

此外,哈希通常的实现需要取模,不过也可以用无符号整型自然溢出。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
using namespace std;

const int N = 2e5 + 5;

vector<int> G[N], G2[N];

int t, n;
using ull = unsigned long long;

ull Hashing[N];
const ull P = 182930841;

ull shift(ull x)
{
	x ^= P;
	x ^= (x << 7);
	x ^= (x >> 13);
	x ^= (x << 17);
	x ^= P;
	return x;
}

set<int> s1, s2;
int sz[N];

void dfs(int u, int f)
{
	sz[u] = 1;
	int maxn = 0;
	for (int& j : G[u])
	{
		if (j != f)
		{
			dfs(j, u);
			sz[u] += sz[j];
			maxn = max(maxn, sz[j]);
		}
	}
	maxn = max(maxn, n - sz[u]);
	if (maxn <= n / 2)
	{
		s1.insert(u);
	}
}

void dfs2(int u, int f)
{
	sz[u] = 1;
	int maxn = 0;
	for (int& j : G2[u])
	{
		if (j != f)
		{
			dfs2(j, u);
			sz[u] += sz[j];
			maxn = max(maxn, sz[j]);
		}
	}
	maxn = max(maxn, n - sz[u]);
	if (maxn <= n / 2)
	{
		s2.insert(u);
	}
}

void Tree_Hashing_1(int u, int f)
{
	Hashing[u] = 1;
	for (int j : G[u])
	{
		if (j != f)
		{
			Tree_Hashing_1(j, u);
			Hashing[u] += shift(Hashing[j]);
		}
	}
}

void Tree_Hashing_2(int u, int f)
{
	Hashing[u] = 1;
	for (int j : G2[u])
	{
		if (j != f)
		{
			Tree_Hashing_2(j, u);
			Hashing[u] += shift(Hashing[j]);
		}
	}
}

int main()
{
	scanf("%d", &t);
	while (t--)
	{
		s1.clear(), s2.clear();
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) G[i].clear(), G[i].shrink_to_fit(), G2[i].clear(), G2[i].shrink_to_fit();
		for (int i = 1; i < n; i++)
		{
			int u, v;
			scanf("%d%d", &u, &v);
			G[u].emplace_back(v);
			G[v].emplace_back(u);
		}
		for (int i = 1; i < n; i++)
		{
			int u, v;
			scanf("%d%d", &u, &v);
			G2[u].emplace_back(v);
			G2[v].emplace_back(u);
		}
		dfs(1, 1);
		dfs2(1, 1);
		set<int> ss;
		for (int j : s1) Tree_Hashing_1(j, 0), ss.insert(Hashing[j]);
		for (int j : s2)
		{
			Tree_Hashing_2(j, 0);
			if (ss.count(Hashing[j]))
			{
				printf("YES\n");
				goto W;
			}
		}
		printf("NO\n");
	W:;
	}
	return 0;
}
posted @   HappyBobb  阅读(23)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示