BZOJ4337:[BJOI2015]树的同构(树hash)
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
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
1
3
1
HINT
【样例解释】
编号为1, 2, 4 的树是同构的。编号为3 的树只与它自身同构。
100% 的数据中,1 ≤ N, M ≤ 50。
Solution
树hash,x的子树计算方法为$\sum hash[i]*val[i]$,其中hash[i]表示的是x的儿子的第i大的哈希值,val[i]是随机的一组很大的数。
Code
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 }