树同构/树哈希

题目

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

树的同构与哈希

判断两棵树是否同构,可以用树哈希实现,两颗树的哈希值一样时,我们可以判定他们同构。树的哈希只在有根树中有意义。当要判定两棵树是否同构时,我们可以选择它们的重心为根进行哈希。时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

关于树哈希,用的比较多的方法是将子树从小到大排序,对于第 i i i个子树,乘上第 i i i个质数

v a l [ y ] = ∑ x ∈ s o n y v a l [ x ] ∗ p r i m e [ s i z e ( x ) ] val[y]=\sum_{x\in {son_y}} val[x]*prime[size(x)] val[y]=xsonyval[x]prime[size(x)]

当然,也有很多哈希方法值得尝试,比如字符串哈希中使用较多的进制哈希写法简单,也很不错。

v a l [ y ] = ∑ x ∈ s o n y v a l [ x ] ∗ b a s e c n t [ x ] val[y]=\sum_{x\in {son_y}} val[x]*base^{cnt[x]} val[y]=xsonyval[x]basecnt[x]

个人比较喜欢的哈希是异或 ,一般瞎hash一下就不会被卡。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 50 + 5;
vector<int> mp[N];
vector<pair<int, int>> centr[N];
int siz[N], mx[N]; 
int n;
int dfs(int u, int fa)
{
    int res = 0;
    siz[u] = 0;
    for (auto v : mp[u])
    {
        if (v == fa)
            continue;
        int val = dfs(v, u);
        siz[u] += val;
        res = max(res, val);
    }
    mx[u] = max(res, n - (siz[u] + 1));
    return siz[u] + 1;
}
void init()
{
    for (int i = 0; i < N; i++)
        mp[i].clear(), mx[i] = 0;
}
int Hash(int u, int fa)
{
    int sum = 1;
    vector<int> res;
    for (auto v : mp[u])
    {
        if (v == fa)
            continue;
        res.emplace_back(Hash(v, u));
    }
    sort(res.begin(), res.end());//必须要排序才能保证正确性!
    for (auto it : res)
        sum += it * 131 ^ 133331;
    return sum;
}
int get(int x)
{
    init();
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        int fa;
        cin >> fa;
        if (fa == 0)
            continue;
        mp[i].emplace_back(fa);
        mp[fa].emplace_back(i);
    }
    dfs(1, -1);
    int maxx = 0x3f3f3f3f;
    for (int i = 1; i <= n; i++)
        maxx = min(maxx, mx[i]);
    for (int i = 1; i <= n; i++)
        if (maxx == mx[i])
            centr[x].emplace_back(make_pair(i, Hash(i, -1)));
    for (auto it : centr[x])
        for (int j = 1; j <= x; j++)
            for (auto it2 : centr[j])
                if (it.second == it2.second)
                    return j;
    return -1;
}
int main()
{
    int m;
    cin >> m;
    for (int i = 1; i <= m; i++)
        cout << get(i) << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值