【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;
}
待到再迷茫时回头望,所有脚印会发出光芒