[BJOI2015]树的同构

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

  • 前置知识

一.树的重心

定义如下:删掉某节点\(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;
}

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

时间: 2024-11-14 12:16:08

[BJOI2015]树的同构的相关文章

[BZOJ4337][BJOI2015]树的同构(树的最小表示法)

4337: BJOI2015 树的同构 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1023  Solved: 436[Submit][Status][Discuss] Description 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相 同,那么这两个树是同

4337. [BJOI2015]树的同构【树哈希】

Description 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相 同,那么这两个树是同构的.也就是说,它们具有相同的形态. 现在,给你M个有根树,请你把它们按同构关系分成若干个等价类. Input 第一行,一个整数M. 接下来M行,每行包含若干个整数,表示一个树.第一个整数N表示点数.接下来N 个整数,依次

【树hs】[BJOI2015]树的同构

题目描述 树是一种很常见的数据结构. 我们把N个点,N-1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相 同,那么这两个树是同构的.也就是说,它们具有相同的形态. 现在,给你M个有根树,请你把它们按同构关系分成若干个等价类. 输入格式 第一行,一个整数M. 接下来M行,每行包含若干个整数,表示一个树.第一个整数N表示点数.接下来N 个整数,依次表示编号为1到N

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

题面: 树是一种很常见的数据结构. 我们把N个点,N−1条边的连通无向图称为树. 若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树. 对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相同,那么这两个树是同构的.也就是说,它们具有相同的形态. 现在,给你M个有根树,请你把它们按同构关系分成若干个等价类. 无根树Hash... 以树的每一个节点为根,求出Hash值后,存入一个数组里,排序. 比对就像这样 for(int j=1;j<=i;j++

[BJOI2015]树的同构 &amp;&amp; 树哈希教程

题目链接 有根树的哈希 离散数学中对树哈希的描述在这里.大家可以看看. 判断有根树是否同构,可以考虑将有根树编码.而编码过程中,要求保留树形态的特征,同时忽略子树顺序的不同.先来看一看这个方法: 不妨令一棵树的编码是个字符串\(T\). 对于一个点\(u\),先求出\(u\)所有\(son_u\)的编码\(f_{son_u}\),然后将这些编码按字典序从小到大排序得到\(g_{1\cdots k}\).那么\(f_u="0"+\sum\limits g_i+"1"\

BZOJ4337 BJOI2015 树的同构

构造一个靠谱点的树哈希函数,每个树以每个点都为根算一次哈希值,然后归类一下有相同哈希值的树就行了. 代码 1 #include<cstdio> 2 #include<algorithm> 3 #define nc() getchar() 4 #define N 500100 5 int dp,pre[N],p[N],ans[N],tt[N],n,i,m[N],a,j,k; 6 long long q,hash[N],s[N],v[N]; 7 void link(int x,int

SDUT 3340 数据结构实验之二叉树一:树的同构

数据结构实验之二叉树一:树的同构 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. Input 输入数据包含

03-树1 树的同构

给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. 输入格式: 输入给出2棵二叉树树的信息.对于每棵树,首先在一行中给出一个非负整数NN (\le 10≤10),即该树的结点数(此时假设结点从0到N-1N−1编号):随后NN行,第ii行对应编号第ii个结点,给出该结点中存储的

5-3 树的同构 (25分)

5-3 树的同构   (25分) 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. 输入格式: 输入给出2棵二叉树树的信息.对于每棵树,首先在一行中给出一个非负整数NN (\le 10≤10),即该树的结点数(此时假设结点从0到N-1N?1编号):随后NN行,第i