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

这篇博客介绍了如何解决树同构问题,主要方法包括树哈希和最小表示法。通过树哈希,对于有根树从根节点开始计算哈希值,无根树则找到重心作为根节点进行哈希。文中提供了使用特定哈希方程的代码实现。

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

P5043 【模板】树同构([BJOI2015]树的同构)
题意:输出与第i棵树同构的树的最小编号

题解:树hash或者最小表示法
树hash,有根树直接从根结点开始hash即可,对于无根树,由于每棵树的重心最多有两个,所以找到重心当作根节点开始hash即可。
我这里采用的hash方程为

    sort(g.begin(),g.end());
    for(int i=0;i<g.size();i++){
        ac[u]=(ac[u]*base%mod+g[i])%mod;
    }
    ac[u]=(ac[u]*siz[u])%mod;

完整代码

#include<bits/stdc++.h>

using namespace std;
#define debug(x) cout<<#x<<" is "<<x<<endl;
typedef long long ll;

const int maxn=105;
const int inf=1e9+5;
const int mod=23333;
const int base=233;

struct edge{
    int fr;
    int to;
    int nex;
}e[maxn<<1];

int maxsiz,n,cnt,head[maxn],rt[maxn],siz[maxn],ans[maxn],sonsiz[maxn],ac[maxn];

void adde(int x,int y){
    e[cnt].fr=x;
    e[cnt].to=y;
    e[cnt].nex=head[x];
    head[x]=cnt++;
}

void dfs(int u,int f){
    siz[u]=1;
    int maxx=0;
    for(int i=head[u];i!=-1;i=e[i].nex){
        int v=e[i].to;
        if(f==v)continue;
        dfs(v,u);
        siz[u]+=siz[v];
        maxx=max(maxx,siz[v]);
    }
    maxx=max(maxx,n-siz[u]);
    sonsiz[u]=maxx;
    maxsiz=min(maxsiz,maxx);
}

int sol(int u,int f){
    ac[u]=1;
    vector<int>g;
    siz[u]=1;
    for(int i=head[u];i!=-1;i=e[i].nex){
        int v=e[i].to;
        if(v==f)continue;
        sol(v,u);
        siz[u]+=siz[v];
        g.push_back(ac[v]);
    }
    sort(g.begin(),g.end());
    for(int i=0;i<g.size();i++){
        ac[u]=(ac[u]*base%mod+g[i])%mod;
    }
    ac[u]=(ac[u]*siz[u])%mod;
    return ac[u];
}

int main(){
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&n);
        maxsiz=n+1;
        cnt=0;
        for(int j=1;j<=n;j++)head[j]=-1;
        for(int j=1;j<=n;j++){
            int f;
            scanf("%d",&f);
            if(f){
                adde(j,f);
                adde(f,j);
            }
        }
        dfs(1,0);
        ans[i]=inf;
        for(int j=1;j<=n;j++){
            if(sonsiz[j]==maxsiz){
                ans[i]=min(ans[i],sol(j,0));
            }
        }
        for(int j=1;j<=i;j++){
            if(ans[j]==ans[i]){
                printf("%d\n",j);
                break;
            }
        }
    }
    return 0;
}

最小表示法(待补)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值