[BJOI2015]树的同构
Posted yanshannan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BJOI2015]树的同构相关的知识,希望对你有一定的参考价值。
https://zybuluo.com/ysner/note/1176508
题面
给出各种形态的树,问哪些树互为重构树?
(nleq50)
解析
(method 1)
一开始没注意到不论树有没有根,都要以树的重心为根,根的不同可以改变树的形态,如一棵树变成一条链之类。
树的重心的要求是使子树 最大规模 最小
显然使用树哈希。
[Hash[x]=sum_{异或和}(Hash[son_{1..k}]+Base1)*(sz[x]+Base2)+deep[x]*Base3]
看起来这式子很容易乘爆,我们可以模一个(2^n)(自然溢出也是同一原理),以减少对 大小在模数范围以内 的二进制位的影响。
最后再注意一下找完(root)后要重新统计子树大小。
但在(bzoj)上死都过不了,很想蒯数据#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<algorithm> #define ll long long #define re register #define il inline #define fp(i,a,b) for(re int i=a;i<=b;i++) #define fq(i,a,b) for(re int i=a;i>=b;i--) using namespace std; const int N=105,mod1=498353,mod2=412817,mod=1<<30; int n,h[N],cnt,m,B1=3,B2=7,B3=11,sz[N],vis1[500000],vis2[500000],dp[N],root; ll Hash[N]; struct Edge{int to,next;}e[N<<1]; il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;} il void dfs(re int u,re int fa,re int deep) { //printf("%d %d %d ",u,fa,deep); re ll sum=0;sz[u]=1; for(re int i=h[u];i+1;i=e[i].next) { re int v=e[i].to; if(v==fa) continue; dfs(v,u,deep+1);//printf("%d ",Hash[v]); sum^=Hash[v]; sz[u]+=sz[v]; } Hash[u]^=((sum+B1)*(sz[u]+B2)*(deep+B3)); Hash[u]%=mod; //printf("%lld %d ",Hash[u],u); } il void getroot(re int u,re int fa) { sz[u]=1; for(re int i=h[u];i+1;i=e[i].next) { re int v=e[i].to; if(v==fa) continue; getroot(v,u); sz[u]+=sz[v]; dp[u]=max(dp[u],sz[v]); } dp[u]=max(dp[u],n-dp[u]); if(dp[u]<dp[root]) root=u; else if(dp[u]==dp[root]&&u<root) root=u; } int main() { m=gi(); fp(o,1,m) { memset(h,-1,sizeof(h));cnt=0;memset(Hash,0,sizeof(Hash));memset(dp,0,sizeof(dp));dp[0]=1e9;memset(sz,0,sizeof(sz)); n=gi();root=0; fp(i,1,n) { re int v=gi(); if(v) add(i,v),add(v,i); } getroot(1,0);//printf("%d %d ",o,root); dfs(root,0,1);//printf("%d %lld ",o,Hash[1]); if(vis1[Hash[root]%mod1]&&vis2[Hash[root]%mod2]) printf("%d ",vis1[Hash[root]%mod1]); else printf("%d ",vis1[Hash[root]%mod1]=vis2[Hash[root]%mod2]=o); } return 0; }
(method 2)
在对(sum[v])进行排序后,
哈希方程变为这样((u)为当前节点,(v)为子节点,(p[i])为质数表)
[sum[u]=sum sum[v]*p[i](uin {v})]
当然当前节点也算单独一颗(size=1)的子树,要不然叶节点怎么办。。。
因该式不考虑诸如深度、以该节点为根的子树等因素,我们就要对每个点为根的情况都进行(Hash)值计算,最后排序以后比较是否完全相同即可。#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<algorithm> #define ll long long #define re register #define il inline #define fp(i,a,b) for(re int i=a;i<=b;i++) #define fq(i,a,b) for(re int i=a;i>=b;i--) using namespace std; const int N=105; int n,h[N],cnt,m,p[55]; ll Hash[N][N],dp[N]; struct Edge{int to,next;}e[N<<1]; il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;} il ll gi() { re ll x=0,t=1; re char ch=getchar(); while((ch<‘0‘||ch>‘9‘)&&ch!=‘-‘) ch=getchar(); if(ch==‘-‘) t=-1,ch=getchar(); while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-48,ch=getchar(); return x*t; } il void wri(re int x) { if(x<0) putchar(‘-‘),x=-x; if(x>9) wri(x/10); putchar(x%10+‘0‘); } il void dfs(re int u,re int fa) { re ll top=0,s[55];s[++top]=1; for(re int i=h[u];i+1;i=e[i].next) { re int v=e[i].to; if(v==fa) continue; dfs(v,u); s[++top]=dp[v]; } dp[u]=0;sort(s+1,s+1+top); fp(i,1,top) dp[u]+=s[i]*p[i]; } il void Pre() { re int tot=0; fp(i,41,300) { re int flag=1; fp(j,2,sqrt(i)) if(i%j==0) {flag=0;break;} if(flag) p[++tot]=i; if(tot>50) break; } } int main() { Pre(); m=gi(); fp(o,1,m) { memset(h,-1,sizeof(h));cnt=0;memset(dp,0,sizeof(dp)); n=gi(); fp(i,1,n) { re int v=gi(); if(v) add(i,v),add(v,i); } fp(i,1,n) dfs(i,0),Hash[o][i]=dp[i]; sort(Hash[o]+1,Hash[o]+1+n); //fp(i,1,n) printf("%lld ",Hash[o][i]);puts(""); fp(i,1,o) { re int flag=1; fp(j,1,n) if(Hash[o][j]!=Hash[i][j]) { //printf("%d %d %lld ",i,j,Hash[i][j]); flag=0;break; } if(flag) {printf("%d ",i);break;} } } return 0; }
以上是关于[BJOI2015]树的同构的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 4337[BJOI2015]树的同构 - 括号序列