把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ4337】[BJOI2015] 树的同构(哈希水题)

点此看题面

大致题意: 给你若干棵树,询问和每棵树同一形态的树编号的最小值。

哈希

这种东西显然哈希哈希就好了吧。。。

反正我就对于树上每个点把它的每个子树大小(包括子树外点的个数)排序一边然后哈希起来,然后对于整棵树把所有节点哈希值排序一遍哈希起来,然后开个\(map\)判断有没有出现过就可以了。

本来还以为要调下参的,结果还没反应过来就秒过了。。。(看来我选用的参数果然是幸运数字)

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int Tt,n,rt,ee,lnk[N+5];struct edge {int to,nxt;}e[N];
class TreeHasher
{
	private:
		int s[N+5],Sz[N+5];
		struct Hash//哈希
		{
			#define ull unsigned long long
			#define RU Reg ull
			#define CU Con ull&
			ull x,y;I Hash() {x=y=0;}I Hash(CU a):x(a),y(a){}I Hash(CU a,CU b):x(a),y(b){}
			I bool operator < (Con Hash& o) Con {return x^o.x?x<o.x:y<o.y;}
			I Hash operator + (Con Hash& o) Con {return Hash(x+o.x,y+o.y);}
			I Hash operator - (Con Hash& o) Con {return Hash(x-o.x,y-o.y);}
			I Hash operator * (Con Hash& o) Con {return Hash(x*o.x,y*o.y);}
		}seed,h[N+5];map<pair<Hash,Hash>,int> p;
		I void dfs(CI x)//遍历子树
		{
			RI i;for(Sz[x]=1,i=lnk[x];i;i=e[i].nxt) dfs(e[i].to),Sz[x]+=Sz[e[i].to];//计算Size
			RI t=0;for(i=lnk[x];i;i=e[i].nxt) s[++t]=Sz[e[i].to];x^rt&&(s[++t]=n-Sz[x]);//统计每个子树大小
			for(sort(s+1,s+t+1),h[x]=0,i=1;i<=t;++i) h[x]=h[x]*seed+s[i];//排序后哈希
		}
	public:
		I TreeHasher() {seed=Hash(20050521,302627441);}//初始化
		I void Solve(CI id)
		{
			RI i;Hash Add(0,0),Mul(1,1);
			for(dfs(rt),sort(h+1,h+n+1),i=1;i<=n;++i) Add=Add*seed+h[i],Mul=Mul*h[i];//把整棵树哈希值排序后再哈希
			pair<Hash,Hash> now=make_pair(Add,Mul);printf("%d\n",p[now]?p[now]:p[now]=id);//用map判断有没有出现过
		}
}H;
int main()
{
	RI i,j,x;for(scanf("%d",&Tt),i=1;i<=Tt;++i)
	{
		for(scanf("%d",&n),ee=0,j=1;j<=n;++j) lnk[j]=0;
		for(j=1;j<=n;++j) scanf("%d",&x),x?add(x,j):(rt=j);H.Solve(i);
	}return 0;
}
posted @ 2020-05-20 16:41  TheLostWeak  阅读(119)  评论(0编辑  收藏  举报