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

Description

树是一种很常见的数据结构。

我们把N个点,N-1条边的连通无向图称为树。

若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。

对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相

同,那么这两个树是同构的。也就是说,它们具有相同的形态。

现在,给你M个有根树,请你把它们按同构关系分成若干个等价类。

Input

第一行,一个整数M。

接下来M行,每行包含若干个整数,表示一个树。第一个整数N表示点数。接下来N

个整数,依次表示编号为1到N的每个点的父亲结点的编号。根节点父亲结点编号为0。

Output

输出M行,每行一个整数,表示与每个树同构的树的最小编号。

Sample Input

4
4 0 1 1 2
4 2 0 2 3
4 0 1 1 1
4 0 1 2 3

Sample Output

1
1
3
1

HINT

【样例解释】

编号为1, 2, 4 的树是同构的。编号为3 的树只与它自身同构。

100% 的数据中,1 ≤ N, M ≤ 50。

树hash,x的子树计算方法为$\sum hash[i]*val[i]$,其中hash[i]表示的是x的儿子的第i大的哈希值,val[i]是随机的一组很大的数。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<map>
 5 #include<algorithm>
 6 #define LL long long
 7 #define MOD (998244353)
 8 using namespace std;
 9
10 struct Edge{int to,next;}edge[201];
11 LL T,n,x,ans,hash[101],val[101];
12 int head[101],num_edge;
13 map<LL,LL>Map;
14
15 void add(int u,int v)
16 {
17     edge[++num_edge].to=v;
18     edge[num_edge].next=head[u];
19     head[u]=num_edge;
20 }
21
22 void Dfs(int x,int fa)
23 {
24     LL q[101],tot=0;
25     hash[x]=0;
26     for (int i=head[x]; i; i=edge[i].next)
27         if (edge[i].to!=fa)
28             Dfs(edge[i].to,x),q[++tot]=hash[edge[i].to];
29     if (tot==0){hash[x]=1; return;}
30     sort(q+1,q+tot+1);
31     for (int i=1; i<=tot; ++i)
32         hash[x]=(hash[x]+q[i]*val[i])%MOD;
33 }
34
35 int main()
36 {
37     for (int i=1; i<=50; ++i)
38         val[i]=rand()*233473ll+rand()*19260817ll+rand();
39     scanf("%d",&T);
40     for (int t=1; t<=T; ++t)
41     {
42         scanf("%d",&n);
43         memset(head,0,sizeof(head)); num_edge=0;
44         for (int i=1; i<=n; ++i)
45         {
46             scanf("%d",&x);
47             if (!x) continue;
48             add(x,i), add(i,x);
49         }
50         ans=233;
51         for (int i=1; i<=n; ++i)
52         {
53             Dfs(i,-1);
54             if (!Map[hash[i]]) Map[hash[i]]=t;
55             else Map[hash[i]]=min(Map[hash[i]],(LL)t);
56             ans=min(ans,Map[hash[i]]);
57         }
58         printf("%lld\n",ans);
59     }
60 }

原文地址:https://www.cnblogs.com/refun/p/9552173.html

时间: 2024-10-28 20:15:42

4337. [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完全相 同,那么这两个树是同

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

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

ZOJ 3602 Count the Trees 树的同构 (哈希)

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4705 题意:给出两棵二叉树A和B,问分别处于A中的子树a和处于B中的子树b结构相同的有多少对. 思路:哈希的想法,不同的数字对应的是不同的结构,比如1代表着单独的叶子结点,2代表着有左子树是叶子结点而没有右子树的子树...每出现一种新的子树情形就记录下来,记录的方式是用dfs回溯过程中判断左子树和右子树组成的子树是否出现过(用pair记录子树的情况,也就是左右子树,两个

BZOJ 4337 树的同构

很明显,这应该是一道模版题(因为我很快就在一本书上找到了这道题的模版),不过令我比较奇怪的大概是有根树和无根树的概念,以及在这道题目中根有卵用吗? (看来树这一块的知识还是要补一下). 树的同构很明显应该是用hash来判断的,当然了,不同的人设计的hash函数不同了.这道题正确的应该是要在树的重心上面跑这道题的模版,(如果你要问我树的重心是啥,我只能跟你说,如果我知道的话,下面这份代码就不会把几乎所有的点都跑一次了,但是由于N<=50,M <= 50 很明显这样跑完还是很快的,事实证明也只跑了

【树hs】[BJOI2015]树的同构

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

[BJOI2015]树的同构

介于这道题没大佬发重心的写法,我就来凑个热闹 前置知识 一.树的重心 定义如下:删掉某节点\(i\)后,若剩余\(k\)个连通分量,那么定义\(d(i)\)为这些连通分量中节点数的最大值.所谓重心,就是使得\(d(i)\)最小的节点\(i\). 定理:重心最多有两个 证明:比较感性的理解:一个重心代表一种最优均分的方案,最坏的情况就是左右为难,两个划分同样优 求法: //size[x]为x子树大小 //maxl为最后min{d(i)} inline void DFS(re int x,re in

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

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

ZOJ--3602--Count the Trees【DFS+Hash】树的同构

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3602 题意:给出一棵有n个节点的二叉树和一棵有m个节点的二叉树,给出每个节点的左右子树信息,问这两棵树有几个相同的子树. 思路:树的同构,比赛时没想法,赛后看的别人的解题报告.实际上是给每个节点的左右子树一个哈希值,不用像字符串哈希那么麻烦,直接给每个子树一个数字标记就行了,用map映射每个节点的左子树和右子树信息对应一个标记值,用DFS给两棵树的每个节点都赋一个哈

BZOJ 3899 仙人掌树的同构 仙人掌同构+KMP算法

题目大意:给定一棵仙人掌,求有多少自同构 仙人掌同构问题= = 曾经出过一个判断两个仙人掌是否同构的题,感觉和这个题很类似 首先假设这是一棵树,考虑怎么做 我们首先找到树的重心(如果有两个就在中间加一个点变成一个) 然后把树Hash 对于一棵树 如果某一哈希值的子树有k个 就把答案乘上一个k! 现在变成了仙人掌,那么我把每个环变成一个红点连向环上的所有点,然后把原先环上的边拆除,可以得到一棵树,按树同构做就行了 为了区分红点和普通点的区别,需要为红点设置不同的哈希参数 但是这样有一个BUG,就是