[BJOI2015] 树的同构

bzoj 4337 传送门

这道题很显然是树的哈希裸题。

有根树的哈希很简单,把子节点哈希值搞一搞(有各种搞法)就变成了本节点哈希值。

再用map存一下就OK了。

但是这道题是无根树,怎么选一个根开始深搜计算哈希值呢?

可以使用树的重心。

每棵树最多有两个重心,至少有一个重心。

我们新建一个节点0。

如果只有一个重心r1,就把0和r1连起来。

如果有两个重心r1、r2,就把r1、r2之间的边断开,再分别从0向r1、r2连边。

然后从0开始计算哈希值就好了。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<map>
  5 #define ull unsigned long long
  6 using namespace std;
  7  
  8 int m,n;
  9 int f[55];
 10 int hd[55],nx[105],to[105],cnt;
 11  
 12 map<ull,int>v;
 13  
 14 void add(int af,int at)
 15 {
 16     to[++cnt]=at;
 17     nx[cnt]=hd[af];
 18     hd[af]=cnt;
 19 }
 20  
 21 void build()//建树 
 22 {
 23     memset(hd,0,sizeof(hd));
 24     memset(nx,0,sizeof(nx));
 25     memset(to,0,sizeof(to));
 26     cnt=0;
 27     scanf("%d",&n);
 28     for(int i=1;i<=n;i++)
 29     {
 30         int q;
 31         scanf("%d",&q);
 32         if(q)add(q,i),add(i,q);
 33     }
 34 }
 35  
 36 int sz[55],mx[55];
 37 
 38 //深搜计算最大子树的重量 
 39 void dfs(int p,int fa)
 40 {
 41     sz[p]=1;
 42     for(int i=hd[p];i;i=nx[i])
 43     {
 44         if(to[i]==fa)continue;
 45         dfs(to[i],p);
 46         sz[p]+=sz[to[i]];
 47         mx[p]=max(mx[p],sz[to[i]]);
 48     }
 49     mx[p]=max(mx[p],n-sz[p]);
 50 }
 51  
 52 int r1,r2;
 53  
 54 void weigh()//找重心 
 55 {
 56     memset(sz,0,sizeof(sz));
 57     memset(mx,0,sizeof(mx));
 58     dfs(1,0);
 59     r1=1,r2=1;
 60     for(int i=1;i<=n;i++)
 61     {
 62         if(mx[i]<mx[r1])r1=i;
 63         if(mx[i]==mx[r1])r2=i;
 64     }
 65     add(0,r1),add(r1,0);
 66     //若有两个重心,把它们分别与新节点相连 
 67     if(mx[r1]==mx[r2]&&r1!=r2)add(0,r2),add(r2,0);
 68     //有一个重心 
 69     else r2=r1;
 70 }
 71 
 72 //深搜计算哈希值 
 73 ull cal(int p,int fa)
 74 {
 75     ull ret=1;
 76     ull st[55];
 77     int tp=0;
 78     for(int i=hd[p];i;i=nx[i])
 79     {
 80         if(to[i]==fa)continue;
 81         //两个重心之间的边不能走了 
 82         if(p==r1&&to[i]==r2)continue;
 83         if(p==r2&&to[i]==r1)continue;
 84         st[++tp]=cal(to[i],p);
 85     }
 86     for(int i=1;i<=tp;i++)ret+=st[i]*st[i];
 87     return ret;
 88 }
 89  
 90 int main()
 91 {
 92     scanf("%d",&m);
 93     for(int i=1;i<=m;i++)
 94     {
 95         build();
 96         weigh();
 97         ull h=cal(0,0);
 98         //map存一下 
 99         if(v.find(h)==v.end())v[h]=i;
100         printf("%d
",v[h]);
101     }
102     return 0;
103 }
View Code

把子节点的哈希值的平方加到一起作为本节点的哈希值,就能过。

把子节点的哈希值分别乘上seed的不同次幂再加一起作为本节点的哈希值,就WA。

不知道为什么......

原文地址:https://www.cnblogs.com/cervusy/p/9984645.html