我们有时需要判断一些树是否同构。这时,选择恰当的Hash方式来将树映射成一个便于储存的Hash值(一般是 32 位或 64 位整数)是一个优秀的方案。

树Hash定义在有根树上。判断无根树同构的时候,可以比较重心为根的Hash值或者比较每个点为根的Hash值。

树哈希有很多种哈希方式,下面介绍其中一种:

$f_x$表示$x$为根的子树的Hash值,$son_x$表示$x$的儿子结点集合,$size_y$表示$y$为根的子树规模,$prime(i)$表示第$i$个素数,则

$$
f_x = 1 + \sum_{y\in son_x}{f_y \times prime(size_y)}
$$

注意到我们求得的是子树的Hash值,也就是说只有当根一样时同构的两棵子树 hash 值才相同。如果数据范围较小,我们可以暴力求出以每个点为根时的Hash值,也可以通过up and down树形dp的方式,遍历树两遍求出以每个点为根时的Hash值,排序后比较。

如果数据范围较大,我们可以通过找重心的方式来优化复杂度。(一棵树的重心最多只有两个,分别比较即可)


例题1:洛谷P5043 [模板]树同构 

判断无根树同构,通过两遍dfs树形dp,求出每个点为根时的Hash值,排序后比较即可。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using std::vector;
using std::sort;
const int N = 60;
int f[N], g[N], siz[N];
int n;
struct Edge
{
    int nex, to;
} edge[N<<1];
int head[N], tot;
vector<int> hs[N];
bool isprime[1000];
int prime[N];
void init(int i) {
    tot = 1;
    memset(head, 0, sizeof(head));
    hs[i].clear();
}
void add_edge(int u, int v) {
    edge[tot].to = v;
    edge[tot].nex = head[u];
    head[u] = tot++;
}
void get_prime(int MAX) {
    int x = 0;
    memset(isprime, true, sizeof(isprime));
    for (int i = 2; i < MAX; i++) {
        if (x > 55) break;
        if (isprime[i]) prime[x++] = i;
        for (int j = 0; j < x; j++) {
            if (i * prime[j] >= MAX) break;
            isprime[i * prime[j]] = 0;
            if (i % prime[j] == 0) break;
        }
    }
}
void dfs1(int x, int fa) {
    siz[x] = f[x] = 1;
    for (int i = head[x]; i; i = edge[i].nex) {
        int y = edge[i].to;
        if (y == fa) continue;
        dfs1(y, x);
        f[x] += f[y] * prime[siz[y]];
        siz[x] += siz[y];
    }
}
void dfs2(int x, int fa, int fa_f) {
    g[x] = f[x] + fa_f * prime[n-siz[x]];
    fa_f *= prime[n-siz[x]];
    for (int i = head[x]; i; i = edge[i].nex) {
        int y = edge[i].to;
        if (y == fa) continue;
        dfs2(y, x, fa_f + f[x] - f[y] * prime[siz[y]]);
    }
}
bool Equal(int x, int y) {
    if (hs[x].size() != hs[y].size()) return false;
    for (int i = 0; i < hs[x].size(); i++) {
        if (hs[x][i] != hs[y][i]) return false;
    }
    return true;
}

int main() {
    get_prime(1000);
    int m;
    while (~scanf("%d", &m)) {
        for (int i = 1; i <= m; i++) {
            init(i);
            scanf("%d", &n);
            for (int j = 1, x; j <= n; j++) {
                scanf("%d", &x);
                if (x) add_edge(x, j), add_edge(j, x);
            }
            dfs1(1, 0);
            dfs2(1, 0, 0);
            for (int j = 1; j <= n; j++) hs[i].push_back(g[j]);
            sort(hs[i].begin(), hs[i].end());
        }
        puts("1");
        for (int i = 2; i <= m; i++) {
            for (int j = 1; j <= i; j++) {
                if (Equal(i, j)) {
                    printf("%d\n", j);
                    break;
                }
            }
        }
    }
    return 0;
}
View Code

相关文章:

  • 2021-06-21
  • 2022-01-07
  • 2021-06-28
  • 2021-10-02
  • 2022-01-13
  • 2022-03-01
  • 2022-01-29
猜你喜欢
  • 2021-07-25
  • 2022-12-23
  • 2021-08-24
  • 2022-01-06
  • 2021-12-04
  • 2021-12-13
相关资源
相似解决方案