TREEISO - Tree Isomorphism
如果是有根树,我们只需要从根开始树哈希即可。如果是无根树,一种方案是,依次选取每个点作为根,但复杂度无法接受。当然可以换根 DP,但有一个比较聪明的方法。
找到两棵树的重心进行树哈希。注意重心最多有两个,所以要进行两次树哈希,对哈希值进行对比。
令 为以 为根的子树的哈希值。我们通常可以设计哈希转移:。其中 是哈希函数。哈希函数的具体方法很多,例如 。但是由于哈希本身并不能保证 的正确性,所以哈希函数的设计会很大程度上导致哈希的结果。例如上述的 并不能通过此题。
通常我们使用 Xor-Hash,代码实现如下:
ull shift(ull x)
{
x ^= P;
x ^= (x << 7);
x ^= (x >> 13);
x ^= (x << 17);
x ^= P;
return x;
}
其中 是一个常数,可以通过随机获得,也可以自己设定。
此外,哈希通常的实现需要取模,不过也可以用无符号整型自然溢出。
#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;
}