[BJOI2015] 树的同构
这道题很显然是树的哈希裸题。
有根树的哈希很简单,把子节点哈希值搞一搞(有各种搞法)就变成了本节点哈希值。
再用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\n",v[h]); 101 } 102 return 0; 103 }
把子节点的哈希值的平方加到一起作为本节点的哈希值,就能过。
把子节点的哈希值分别乘上seed的不同次幂再加一起作为本节点的哈希值,就WA。
不知道为什么......