刷题总结——树的同构(bzoj4337 树上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 

Sample Output

1

1

3

1

HINT

【样例解释】

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

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

题解:

通过树上hash解决树的同构问题的模板题···

先说如何进行树上hash,设f[i]为以i为节点的hash值,则

       f[i]=sigma(f[j]*prime[j])  j为son[i]

其中prime为预处理出来的素数表···注意f[j]需要进行排序····

然后对于两颗待判定的树,将两颗树分别以树上每一个节点为根节点求hash值··将根节点的hash值储存起来排序然后两颗树一一比对··如果完全一样则两棵树就一样

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=55;
int hash[N][N],n,que[N];
int tot,fst[N],nxt[N*2],go[N*2],f[N];
int prime[]={0,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317};
inline void comb(int a,int b)
{
  nxt[++tot]=fst[a],fst[a]=tot,go[tot]=b;
  nxt[++tot]=fst[b],fst[b]=tot,go[tot]=a;
}
inline void dfs(int u,int fa)
{
  unsigned int st[N];int tot=0;  
  st[++tot]=1;
  for(int e=fst[u];e;e=nxt[e])
  {
    int v=go[e];if(v==fa)  continue;
    dfs(v,u);
    st[++tot]=f[v];
  }
  f[u]=0;sort(st+1,st+tot+1);
  for(int i=1;i<=tot;i++)  f[u]+=st[i]*prime[i];
}
int main()
{
 // freopen("a.in","r",stdin);
  scanf("%d",&n);int a,b;
  for(int i=1;i<=n;i++)
  {
    scanf("%d",&a);
    memset(fst,0,sizeof(fst));tot=0;
    for(int j=1;j<=a;j++)  
    {
      scanf("%d",&b);
      if(b) comb(j,b);
    }
    for(int j=1;j<=a;j++)
    {
      dfs(j,0);hash[i][j]=f[j];
    }
    sort(hash[i]+1,hash[i]+a+1);
    for(int j=1;j<=i;j++)
    {
      bool flag=true;
      for(int k=1;k<=a;k++)
        if(hash[j][k]!=hash[i][k])  
        {  
          flag=false;break;
        }
      if(flag==true)  
      {       
        printf("%d\n",j);
        break;
      }
    }
  }
  return 0;
}

 

posted @ 2017-10-06 22:41  AseanA  阅读(1238)  评论(0编辑  收藏  举报