(笔记)哈希 树哈希

哈希

basic

将字符串转换为模意义下的哈希编码的方法。

具体地,给每个字母赋予一个权值,再当成\(base\)进制处理。主要用途是判断两种状态是否等价。

例:\(abba\)中令\(a\)\(0\)\(b\)\(1\),则\(hash(babba)=1\times base^4 +0\times base^3+1\times base^2+1\times base^1+0\times base^0\)

树哈希

构造一个哈希函数\(f(x)\)使得对于以\(x\)为根节点的子树,有根树本质相同时哈希值也相同,即儿子的顺序与哈希值无关。

考虑到如上限制,不难想到用满足交换律的运算构造。

一般地,可以有\(f(x)=\sum _{v\in subtree(x)} f(v)\)\(f(x)=\prod _{v\in subtree(x)} f(v)\)

考虑到冲突可能性较大,可优化为\(f(x)=p1+p2^{soncnt(x)}\prod _{v\in subtree(x)} f(v)\)等不同形式。

例题:P5043 【模板】树同构([BJOI2015]树的同构)

代码贴贴:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD=998244353,p1=1331,p2=2333;
const int N=55,INF=1e9;
int n,m;
vector<int>G[N][N];
LL fac[N];
map<LL,int>mp;
LL hs[N];
LL siz[N],son[N];
inline LL tms(LL x,LL y){
	return (x%MOD)*(y%MOD)%MOD;
}
inline LL pls(LL x,LL y){
	return ((x%MOD)+(y%MOD))%MOD;
}
int rt1,rt2;
int rt;
LL mx[N];
void fr(int u,int fa,int id){
	siz[u]=1;
	mx[u]=0;
	for(int v:G[id][u]){
		if(v==fa)continue;
		fr(v,u,id);
		siz[u]+=siz[v];
		mx[u]=max(siz[v],mx[u]);
	}
	mx[u]=max(mx[u],n-siz[u]);
	if(mx[rt1]>mx[u])rt2=rt1,rt1=u;
	else if(mx[rt2]>mx[u])rt2=u;
}
LL dfs(int u,int fa,int id){
	siz[u]=1;
	LL ct=1;
	son[u]=0;
	for(int v:G[id][u]){
		if(v==fa)continue;
		son[u]++;
		ct=tms(ct,dfs(v,u,id));
		siz[u]+=siz[v];
	}
	ct=tms(ct,fac[son[u]]);
	ct=pls(ct,p1);
	return ct;
}
LL mk(int id){
	return dfs(rt,0,id);
}
LL hs1,hs2;
void init(int id){
	mx[0]=INF;
	rt1=rt2=0;
	fr(rt,0,id);
	rt=rt1;
	hs1=mk(id);
	rt=rt2;
	mk(id);
	hs2=mk(id);
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>m;
	fac[0]=1;
	for(int i=1;i<=50;i++){
		fac[i]=tms(fac[i-1],p2);
	}
	for(int i=1;i<=m;i++){
		cin>>n;
		for(int v=1;v<=n;v++){
			int fa;
			cin>>fa;
			if(!fa){rt=v;continue;}
			G[i][fa].push_back(v);
			G[i][v].push_back(fa);
		}
		init(i);
		if(mp[hs1]&&mp[hs2]){
			mp[hs1]=min(mp[hs1],mp[hs2]);
			mp[hs2]=mp[hs1];
			cout<<mp[hs1]<<'\n';
		}
		else if(mp[hs1]){
			cout<<mp[hs1]<<'\n';
		}
		else if(mp[hs2]){
			cout<<mp[hs2]<<'\n';
		}
		else {
			mp[hs1]=mp[hs2]=i;
			cout<<mp[hs1]<<'\n';
		}
	}
	return 0;
}

线段树 + 哈希

P2757 [国家集训队] 等差子序列

本题主要考察的是转换思想,注意到只需要三项就可以构成基本等差数列,考虑枚举中间项,则左右两项需要关于中间项在数值上对称即可。

通过这个性质可以在值域上建一个桶,每次按顺序加入一个数后对半折看是否有重合位置不相等的地方,可以用线段树+哈希维护。

posted @ 2025-04-24 14:44  TBSF_0207  阅读(21)  评论(0)    收藏  举报