【算法竞赛学习笔记】用不着的图论之树哈希

19 篇文章 0 订阅
本文介绍了树哈希的概念及其在判断树同构问题中的应用。通过ACM竞赛题目实例,展示了如何利用树哈希判断无根树是否同构,并提供了两遍DFS树形DP的方法来实现。同时,给出了一个独特的树叶问题,探讨了如何在树结构中寻找特定性质的节点。这些算法在解决复杂树结构问题时具有较高的效率。
摘要由CSDN通过智能技术生成

title : 树Hash
date : 2021-10-6
tags : ACM,图论
author : Linno


在这里插入图片描述

树哈希

应用

判断树是否同构

思想

判断无根树同构的时候,可以比较重心为根的Hash值或者比较每个点为根的Hash值。

树哈希的表示方法

f x = 1 + ∑ y ∈ s o n x f y × p r i m e ( s i z e y ) f_x=1+\sum_{y\in son_x}f_y×prime(size_y) fx=1+ysonxfy×prime(sizey)

[NOIP2018 普及组] 对称二叉树

题目链接:https://www.luogu.com.cn/problem/P5018

#include<bits/stdc++.h>
#define int long long
#define V1 (999999751)
#define V2 (299999827)
#define V3 (100000007)
#define md (89999794200117649)
#define mdd (999999786000011449)
using namespace std;
const int maxn=1e6+7;

int n,ans,val[maxn],rs[maxn],ls[maxn];
int he[maxn],hal[maxn],Hal[maxn],har[maxn],Har[maxn];

void dfs(int x,int fa){
	if(ls[x]>0) dfs(ls[x],x);
	if(rs[x]>0) dfs(rs[x],x);
	he[x]=he[ls[x]]+he[rs[x]]+1;
	if(he[ls[x]]==he[rs[x]]&&hal[ls[x]]==har[rs[x]]&&Hal[ls[x]]==Har[rs[x]]){
		ans=max(ans,he[x]);
	}
	hal[x]=hal[ls[x]]*V1+val[x]*V2+hal[rs[x]]*V3;
	Hal[x]=Hal[ls[x]]*V1+val[x]*V2+Hal[rs[x]]*V3;
	hal[x]%=md;
	Hal[x]%=mdd;
	har[x]=har[rs[x]]*V1+val[x]*V2+har[ls[x]]*V3;
	Har[x]=Har[rs[x]]*V1+val[x]*V2+Har[ls[x]]*V3;
	har[x]%=md;
	Har[x]%=mdd;
}

signed main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>val[i];
	for(int i=1;i<=n;i++){
		cin>>ls[i]>>rs[i];
	}
	dfs(1,0);
	cout<<ans;
	return 0;
}
【模板】树同构

题目链接:https://www.luogu.com.cn/problem/P5043

判断无根树同构,通过两遍dfs树形dp,求出每个点为根时的Hash值,排序后比较即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+7;
const int mod=1e9+7;
const int N=55;

bool is_pri[maxn];
int pri[maxn];
int sz[N],f[N],g[N];
int n,m;
vector<int>hs[N];
vector<int>G[N];

void get_pri(int N){
	int cnt=0;
	memset(is_pri,1,sizeof(is_pri));
	for(int i=2;i<N;i++){
		if(is_pri[i]) pri[++cnt]=i;
		for(int j=1;j<=cnt&&pri[j]*i<=N;j++){
			is_pri[i*pri[j]]=0;
			if(i%pri[j]==0) break;
		}
	}
}

void dfs1(int x,int fa){
	sz[x]=f[x]=1;
	for(int i=0;i<G[x].size();i++){
		int to=G[x][i];
		if(to==fa) continue;
		dfs1(to,x);
		f[x]+=f[to]*pri[sz[to]];
		sz[x]+=sz[to];
	}
}

void dfs2(int x,int fa,int fa_f){
	g[x]=f[x]+fa_f*pri[n-sz[x]];
	fa_f*=pri[n-sz[x]];
	for(int i=0;i<G[x].size();i++){
		int to=G[x][i];
		if(to==fa) continue;
		dfs2(to,x,fa_f+f[x]-f[to]*pri[sz[to]]);
	}
}

bool Equal(int x,int y){
	if(hs[x].size()!=hs[y].size()) return false;
	for(int i=0;i<hs[x].size();i++){
		if(hs[x][i]!=hs[y][i]) return false;
	}
	return true;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	get_pri(10000);
	cin>>m;
	int fa;
	for(int i=1;i<=m;i++){
		cin>>n;
		for(int j=1;j<=n;j++) G[j].clear();
		for(int j=1;j<=n;j++){
			cin>>fa;
			if(fa){
				G[j].push_back(fa);
				G[fa].push_back(j);
			}
		}
		dfs1(1,0);
		dfs2(1,0,0);
		for(int j=1;j<=n;j++) hs[i].push_back(g[j]);
		sort(hs[i].begin(),hs[i].end()); 
	}
	puts("1");
	for(int i=2;i<=m;i++){
		for(int j=1;j<=i;j++){
			if(Equal(i,j)){
				printf("%d\n",j);
				break;
			}
		}
	}
	return 0;
}
[JSOI2016]独特的树叶

题目链接https://www.luogu.com.cn/problem/P4323

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=100010;
bool is_pri[2000010];
int pri[N];
int in[N],near[N];

void get_pri(int MX){
	int cnt=0;
	memset(is_pri,1,sizeof(is_pri));
	for(int i=2;i<MX;i++){
		if(is_pri[i]) pri[++cnt]=i;
		for(int j=1;j<=cnt&&pri[j]*i<MX;j++){
			is_pri[i*pri[j]]=0;
			if(i%pri[j]==0) break;
		}
	}
}

struct Edge{
	int nxt,to;
};

struct Tree{
	int f[N],g[N],sz[N],head[N];
	int n,tot;
	Edge edge[N<<1];
	void init(int nn){
		n=nn;
		tot=1;
		memset(head,0,sizeof(head));
	}
	void addedge(int u,int v){
		edge[tot].to=v;
		edge[tot].nxt=head[u];
		head[u]=tot++;
	}
	void dfs1(int x,int fa){
		sz[x]=f[x]=1;
		for(int i=head[x];i;i=edge[i].nxt){
			int y=edge[i].to;
			if(y==fa) continue;
			dfs1(y,x);
			f[x]+=f[y]*pri[sz[y]];
			sz[x]+=sz[y];
		}
	}
	void dfs2(int x,int fa,int fa_f){
		g[x]=f[x]+fa_f*pri[n-sz[x]];
		fa_f*=pri[n-sz[x]];
		for(int i=head[x];i;i=edge[i].nxt){
			int y=edge[i].to;
			if(y==fa) continue;
			dfs2(y,x,fa_f+f[x]-f[y]*pri[sz[y]]);
		}
	}
}tr1,tr2;

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	get_pri(2000000);
	int n;cin>>n;
	tr1.init(n);
	tr2.init(n+1);
	memset(in,0,sizeof(in));
	unordered_set<int>se;
	for(int i=0,u,v;i<n-1;i++){
		cin>>u>>v;
		tr1.addedge(u,v);
		tr1.addedge(v,u);
	}
	for(int i=0,u,v;i<n;i++){
		cin>>u>>v;
		tr2.addedge(u,v);
		tr2.addedge(v,u);
		++in[u],++in[v];
		near[u]=v,near[v]=u;
	}
	tr1.dfs1(1,0);
	tr1.dfs2(1,0,0);
	for(int i=1;i<=n;i++) se.insert(tr1.g[i]);
	tr2.dfs1(1,0);
	tr2.dfs2(1,0,0);
	for(int i=1;i<=n+1;i++){
		if(in[i]!=1) continue;
		if(se.count(tr2.g[near[i]]-2)){
			printf("%d\n",i);
			break;
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值