一千个读者,就有一千个哈姆雷特
一千个OIer,就有几千种哈希方法
哈希当然都是不完美的,很容易被Hack
多哈希几次呢?(比如质数不同,哈希方式不同)
树哈希
树哈希的毒瘤方法
显然我们需要让儿子有序
否则子树就被加上了有序性
H
H
H表示哈希值
r
k
rk
rk表示节点在父亲子树中的排名
s
s
s是一个种子
p
p
p是质数
法一
H u = s i z u × ∑ v ∈ s o n u H v × s r k v H_u=siz_u\times \sum_{v\in son_u}H_{v}\times s^{rk_v} Hu=sizu×v∈sonu∑Hv×srkv
法二
H u = ⨁ v ∈ s o n u H v × s + s i z v H_u=\bigoplus_{v\in son_u} H_v\times s+siz_v Hu=v∈sonu⨁Hv×s+sizv
法三
H u = 1 + ∑ v ∈ s o n u H v × p s i z v H_u=1+\sum_{v\in son_u} H_v\times p_{siz_v} Hu=1+v∈sonu∑Hv×psizv
题
【模板】树同构([BJOI2015]树的同构)
无根树不是个好东西
需要以每一个节点为根哈希一下
不过没关系,数据小
map
是个好东西
然而A5W5的哈希冲突就很淦
要每个点为根的哈希值都相同才说明同构
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1001,s=2333;
vector<int>G[N];
int n,h[N][N];
int Hash(int u,int fa){
int q[N],top=0,ans=N;
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fa) continue;
q[++top]=Hash(v,u);
}
sort(q+1,q+top+1);
for(int i=1;i<=top;++i)
ans=ans*s+q[i];
return ans*s+N+1;
}
signed main(){
int M=in;
for(int id=1;id<=M;++id){
n=in;
for(int i=0;i<N;++i) G[i].clear();
for(int i=1;i<=n;++i){
int u=in;
if(!u) continue;
G[u].push_back(i);
G[i].push_back(u);
}
for(int i=1;i<=n;++i)
h[id][i]=Hash(i,0);
sort(h[id]+1,h[id]+n+1);
for(int i=1;i<=id;++i){
int k=0;
while(k<=n){
++k;
if(k>n) break;
if(h[id][k]!=h[i][k]) break;
}
if(k>n){printf("%llu\n",i);break;}
}
}
// for(int i=1;i<=M;++i){
// for(int j=1;j<=n;++j)
// cout<<h[i][j]<<" ";
// cout<<endl;
// }
return 0;
}