记左儿子对应的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;
}