码迷,mamicode.com
首页 > 其他好文 > 详细

[BJOI2015]树的同构

时间:2019-10-04 20:51:33      阅读:99      评论:0      收藏:0      [点我收藏+]

标签:pre   判断   连通   tin   好的   break   解决   oid   核心   

介于这道题没大佬发重心的写法,我就来凑个热闹

  • 前置知识

一.树的重心

定义如下:删掉某节点\(i\)后,若剩余\(k\)个连通分量,那么定义\(d(i)\)为这些连通分量中节点数的最大值。所谓重心,就是使得\(d(i)\)最小的节点\(i\)

定理:重心最多有两个

证明:比较感性的理解:一个重心代表一种最优均分的方案,最坏的情况就是左右为难,两个划分同样优

求法:

//size[x]为x子树大小
//maxl为最后min{d(i)}
inline void DFS(re int x,re int fa){
    size[x]=1;
    re int i,y,res=0;
    for(i=h[x];i;i=e[i].next){
        y=e[i].to;if(y==fa)continue;
        DFS(y,x);
        size[x]+=size[y];
        res=max(res,size[y]);
    }
    res=max(res,n-size[x]);d[x]=res;
    maxl=min(maxl,res);
}

最后比较一下重心即可出来

//每次调用时
maxl=INF;DFS(1,0);
for(j=1;j<=n;++j){if(d[j]==maxl)rt[++tot]=j;}

二.树的同构

  • 引入:\(POJ1635\)

  • 概念:我们对一棵树的最小\(01\)欧拉序(\(0->\)\(1->\)出)称为其最小表示,判断两树是否同构可比较它们的最小表示

  • 实现:

    • 法一:根据定义直接递归解决

    • 法二:对于更大的数据我们采用树\(Hash\)

    核心:

    inline int Solve(re int x,re int fa){
    re int i,y,res=2333;
    re vector<int > t;
    for(i=h[x];i;i=e[i].next){
        y=e[i].to;if(y==fa)continue;
        t.push_back(Solve(y,x));
    }
    sort(t.begin(),t.end());
    for(i=0;i<t.size();++i)res=((res*Mul)^t[i])%Mod;
    return res;
    }

对这道题:考虑对两棵同构的无根树,我们只需要比较其以树上固定点(即不会因为编号方式改变改变的点)为根的\(Hash\)值,重心则是一个很好的例子

步骤:先找每棵树重心(最多两个),以重心为根来比较

注意一些细节:

由于重心的编号顺序不一定因此我们每次都要存下所有重心为根\(Hash\)

还是放一下代码:

inline void DFS(re int x,re int fa){
    size[x]=1;
    re int i,y,res=0;
    for(i=h[x];i;i=e[i].next){
        y=e[i].to;if(y==fa)continue;
        DFS(y,x);
        size[x]+=size[y];
        res=max(res,size[y]);
    }
    res=max(res,n-size[x]);d[x]=res;
    maxl=min(maxl,res);
}
inline int Solve(re int x,re int fa){
    re int i,y,res=2333;
    re vector<int > t;
    for(i=h[x];i;i=e[i].next){
        y=e[i].to;if(y==fa)continue;
        t.push_back(Solve(y,x));
    }
    sort(t.begin(),t.end());
    for(i=0;i<t.size();++i)res=((res*Mul)^t[i])%Mod;
    return res;
}
int main(void){
    re int i,j,x;
    scanf("%d",&m);
    memset(ans,INF,sizeof ans);
    for(i=1;i<=m;++i){
        scanf("%d",&n);
        cnt=0;memset(h,0,sizeof h);tot=0;
        for(j=1;j<=n;++j){scanf("%d",&x);if(x){AddEdge(j,x);AddEdge(x,j);}}
        maxl=INF;DFS(1,0);
        for(j=1;j<=n;++j){if(d[j]==maxl)rt[++tot]=j;}
        for(j=1;j<=tot;++j)ans[i]=min(ans[i],Solve(rt[j],0));
        for(j=1;j<=i;++j)if(ans[j]==ans[i]){printf("%d\n",j);break;}
    }
    return 0;
}

[BJOI2015]树的同构

标签:pre   判断   连通   tin   好的   break   解决   oid   核心   

原文地址:https://www.cnblogs.com/66t6/p/11622928.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!