zoj 3602 Count the Trees 二叉树HASH 浙江省第9届大学生程序设计竞赛 第三题

6 篇文章 0 订阅
3 篇文章 0 订阅

记左儿子对应的hash值为a,右儿子对应的hash值为b,使val=hash(a,b) {hash(a,b)=find(a,b)?hashval:cnt++;},则每种形状的树的表示是存在且唯一的。

首先对每一种形状树是显然存在对应hash值的。

条件a:若树rt的子树a与子树b唯一,则树rt唯一。(显然成立,值为第一次计算到时对应的cnt)

条件b:叶子节点的hash值唯一(初始化成一样就行了)

接下来就可以用hash或map来维护hash(a,b)=find(a,b)?hashval:cnt++;复杂度前面近似O(n) 后者n*log(n),都可以解决了

细节处理:当左儿子或右儿子不存在时,若用hash计算hash(a,b)=find(a,b)?hashval:cnt++,则要把不存在的儿子从标号从-1处理到0,这样就可以用id[l[rt]]与id[r[rt]]表示了

trick:memset(id,0,sizeof(id))后要把id[0]赋值为0,不然计算时会出现hash(-1,b),hash(a,-1),hash(-1,-1),对设计出高效的hash函数会有一定影响。

附hash代码:3464kb 260ms 1152B

#include<cstdio>
#include<cstring>
const int maxn=111111;
const int  hsz=132000;
bool save;
int l[maxn],r[maxn];
int hx[hsz],hy[hsz],hv[hsz];//hash_buff
#define runHash(x,y) int z=((x<<9)^y)&131071;while(~hx[z]&&(hx[z]!=x||hy[z]!=y)) z=(1+z)&131071;
int id[maxn],cnt,val[maxn];
long long ans;
void dfs(int rt){
    int &a=id[l[rt]],&b=id[r[rt]];
    if(l[rt]&&-1==a) dfs(l[rt]);
    if(r[rt]&&-1==b) dfs(r[rt]);
    runHash(a,b);
    if(save){
	if(-1==hx[z]) hx[z]=a,hy[z]=b,hv[z]=cnt++;
	val[id[rt]=hv[z]]++;
    }
    else{
	if(-1==hx[z]) return;
	ans+=val[id[rt]=hv[z]];
    }
}
int main(){
    int t;scanf("%d",&t);
    while(t--)
    {
	int n,m;cnt=1;ans=0;
	memset(val,0,sizeof(val));
	memset(hx,-1,sizeof(hx));
	scanf("%d%d",&n,&m);

	for(int i=1;i<=n;i++){
	    scanf("%d%d",l+i,r+i);
	    if(l[i]==-1) l[i]=0;
	    if(r[i]==-1) r[i]=0;
	}
	memset(id+1,-1,n*4);save=1;
	for(int i=1;i<=n;i++) if(id[i]==-1) dfs(i);

	for(int i=1;i<=m;i++){
	    scanf("%d%d",l+i,r+i);
	    if(l[i]==-1) l[i]=0;
	    if(r[i]==-1) r[i]=0;
	}
	memset(id+1,-1,4*m);save=0;
	for(int i=1;i<=m;i++) if(id[i]==-1) dfs(i);

	printf("%lld\n",ans);
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值