hdu5732(树哈希+树的重构+树的重心)

本文介绍了一种判断两棵树是否同构的方法,通过找到树的重心并使用哈希技术进行比对。讨论了如何求解树的重心,并详细解释了基于素数的哈希算法,以及如何改进算法避免哈希冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

给定一棵树,这两棵树肯定是同构的。

问你,第一棵树的每个节点,可以对应第二个树的那个节点。 显然对应方法不唯一,SPJ来检测结果正确。



方法:

首先找树的重心, 树的重心最多2个。


一个重心的情况很多,两个重心的情况如图:



有人说这个图太对称了……


那给个不对称的。

 这个图很重要……涉及到一些奇怪的情况



求树的重心,是一个简单的tree dp


f[i]表示i如果为根(为整棵树的根的情况下),所有儿子节点的size的max。

s[i]表示在做DP中,i的所有儿子的节点总数。

f[i] = max(s[j],  n-sum(s[j])-1)  其中j为i的儿子。


然后找f[]数组中元素的最小值,就是树的重心了。

如果第一棵树的重心为A,B。 第二颗数的重心是C,D

那么就检查以A为根,以C为根的两棵树是否同构,再检查(A,D),(B,C),(B,D)是否同构。同构则相对着输出即可。


题解这里判断同构的是给树进行哈希。给的方案是:

(我理解的……)

照抄题解“

这里提供一种方法:首先求解树的中点,然后将中点作为根。只有一个结点的子树哈希值为 1。选一个比较大的质数P和一个特别大的质数Q。对于每一颗树,把它的所有子树的哈希 值排序。然后hash=sum(P^{i}*hash[i])\%Qhash=sum(Pihash[i])%Q,就能算出来总体的哈希值。有两个中点的树两个 中点都试一下。为了保险可以检查下哈希值有没有重的。

叶子节点的哈希值为1,假设要求K节点的哈希值。

K有儿子节点A,B,C。并且A<=B<=C(排序好了)

//求哈希值的方法!!!!

hash[k] =  (P^1*hash[A])+ (P^2*hash[B])+(P^3*hash[C]) % Q;

hash=sum(P^{i}*hash[i])\%Qhash=sum(Pihash[i])%Q;

P,Q为素数。




#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<set>
using namespace std;
typedef long long ll;

const ll pp[]={435617,999983,327827,674249,986191};
const ll mod=1e9+7;
int n;
ll cnt;
map<string,ll> mapp;
string s1[100010],s2[100010];
vector<ll> g1[100010],g2[100010];

ll Pow(ll a,ll b,ll M)
{
    ll ans=1;
    a=a%M;
    while(b)
    {
        if(b&1)
            ans=ans*a%M;
        a=a*a%M;
        b/=2;
    }
    return ans;
}

ll get(char a[])
{
    string x="";
    for(int i=0;a[i]!=0;x+=a[i++]);
    //cout<<x;
    if(mapp.find(x)==mapp.end())
    {
        mapp[x]=++cnt;
    }
    else
        return mapp[x];
    return cnt;
}

void init()
{
    cnt=0;
    mapp.clear();
    for(int i=0;i<=n;i++)
    {
        g1[i].clear();
        g2[i].clear();
    }
    char a[102],b[102];
    for(int i=1;i<n;i++)
    {
        scanf("%s%s",a,b);
        ll A=get(a);
        s1[A]=a;
        ll B=get(b);
        s1[B]=b;
        g1[A].push_back(B);
        g1[B].push_back(A);
       // printf("%lld %lld\n",A,B);
    }
    for(int i=1;i<n;i++)
    {
        scanf("%s%s",a,b);
        ll A=get(a);
        s2[A]=a;
        ll B=get(b);
        s2[B]=b;
        g2[A].push_back(B);
        g2[B].push_back(A);
        //printf("%lld %lld\n",A,B);
    }
}

ll dp(int now,vector<ll> g[],ll f[],int father)
{
    ll sum=0;
    f[now]=0;
    if(g[now].size()==0)
    {
        return 1;
    }
    for(int i=0;i<g[now].size();i++)
    {
        int v=g[now][i];
        if(v!=father)
        {
            ll t=dp(v,g,f,now);
            sum+=t;
            f[now]=max(f[now],t);
        }
    }
    if(now!=father)
        f[now]=max(f[now],n-sum-1);
    return sum+1;
}
ll f1[100010],f2[100010];
ll f3[100010];
vector<ll> zhong1,zhong2;
struct node
{
    ll Hash,id;
    node(){}
    node(ll h,ll i)
    {
        Hash=h;
        id=i;
    }

};
bool operator <(node a,node b)
{
    return a.Hash<b.Hash;
}
vector<node> tmp[100010];
vector<ll> g3[100010],g4[100010];
ll myhash(int now,vector<ll> g[],ll f[],int fa,vector<ll> h[])
{
    //cout<<"Dsdsd";
    if(g[now].size()==1&&g[now][0]==fa)
    {
        return f[now]=1;
    }
    ll hash_=0;
    tmp[now].clear();
    for(int i=0;i<g[now].size();i++)
    {
        int v=g[now][i];
        if(v!=fa)
        {
            ll t=myhash(v,g,f,now,h);
            tmp[now].push_back(node(t,v));
        }
    }
    sort(tmp[now].begin(),tmp[now].end());
    for(int i=0;i<tmp[now].size();i++)
    {
        h[now].push_back(tmp[now][i].id);
        hash_=(hash_+(tmp[now][i].Hash*Pow(pp[i%5],i+1,mod)))%mod;
    }
    f[now]=hash_;
    return f[now];
}
void print(int a,int b)
{
   // printf("fsfsf");
    printf("%s %s\n",s1[a].c_str(),s2[b].c_str());
    for(int i=0;i<g3[a].size();i++)
    {
        print(g3[a][i],g4[b][i]);
    }
}
bool check(int a,int b)
{
    //cout<<"dsds";
    for(int i=0;i<=n;i++)
    {
        g3[i].clear();
        g4[i].clear();
    }
    myhash(a,g1,f1,0,g3);
    myhash(b,g2,f2,0,g4);
    if(f1[a]==f2[b])
    {
        print(a,b);
        return true;
    }
    return false;
}
void solve()
{
    /*
    第一步,求树的重心
    */
    dp(1,g1,f1,1);
    dp(1,g2,f2,1);
//    for(int i=1;i<=n;i++)
//    {
//        printf("f2[%d]==%d\n",i,f2[i]);
//    }
    zhong1.clear();
    zhong2.clear();
    memmove(f3,f1,sizeof(f3));
    sort(f3+1,f3+1+n);
    ll tmp=f3[1];
    for(int i=1;i<=n;i++)
    {
       // cout<<f1[i]<<tmp<<f2[i]<<endl;
        if(f1[i]==tmp)
            zhong1.push_back(i);
        if(f2[i]==tmp)
            zhong2.push_back(i);
    }
    for(int i=0;i<zhong1.size();i++)
        for(int j=0;j<zhong2.size();j++)
            if(check(zhong1[i],zhong2[j]))
                return;
}

int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();//存树
        solve();
    }
    return 0;
}


我上面图上的那个情况,按照我理解的题解的做法,不管你素数取多少,都会重复。导致不管选哪一对重心,都会导致哈希值相同……



然后我给哈希函数改一下,排序好的A,B,C.... 并不都用P这一个素数,每5个数字循环一次,用5个素数来

//最稳定!!(保证不要重复)

hash[k] =  (P[1]^1*hash[A])+ (P[2]^2*hash[B])+(P[3]^3*hash[C])  + ....+ [P[I%5+1]^I * hash[?]] % Q

这样…… 然后就AC了。 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值