树哈希 ----【重庆市2015NOIP模拟赛】仔细的检查

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20180602_csq/article/details/102503484

【重庆市NOIP模拟赛】仔细的检查

时限: 1 Sec  内存: 128 MB  Special Judge

题目描述

nodgd家里种了一棵树,有一天nodgd比较无聊,就把这棵树画在了一张纸上。另一天nodgd更无聊,就又画了一张。

这时nodgd发现,两次画的顺序是不一样的,这就导致了原本的某一个节点u0在第一幅图中编号为u1,在第二副图中编号为u2。

于是,nodgd决定检查一下他画出的两棵树到底是不是一样的。nodgd已经给每棵树的节点都从1到n进行了编号,即每棵树有n个节点。

如果存在一个1到n的排列p1,p2…pn,对于第一幅图中的任意一条边(i,j),在第二幅图中都能找到一条边(pi,pj),则认为这两幅图中的树是一样的。

输入格式

第一行一个整数n,表示节点的总数。

接下来n−1行,每行两个整数,表示第一幅图中的每一条边。

接下来n−1行,每行两个整数,表示第二幅图中的每一条边。

输出格式

如果两幅图的树是一样的,第一行输出”YES”,接下来1行输出一个1到n的排列p1,p2,……,pn,两个数之间用空格间隔。当多个排列都满足题意时,你可以随便输出一个。

如果两幅图的树是不一样的,只输出一行”NO”。

注意输出的时候不要加引号。

输入样例 

3
1 2
2 3
1 3
3 2

输出样例 

YES
1 3 2

提示

【样例解释1】

肉眼可见,1-2-3和1-3-2显然是一样的两棵树。不过这可能不是唯一的符合题意的排列。

数据范围:n<=100000

题解

树哈希

方法有很多

下面介绍普通的方法

设h[u]为以u为根子树的哈希值,g[u]为以u为根时整棵树的哈希值,bas[x]为一个随机权值数组

则有h[u]=1+h[son]*bas[siz[son]]

g[son]=(g[u]-h[son]*bas[siz[son]])*bas[n-siz[son]]+h[son]

于是我们就可以把以每个点为根的全树哈希值作为判定一个的标志

把A树中的哈希值放入map,映射到一个stack里面,每次查询一下B树中的某个节点哈希值

如果map中存在对应的哈希值,我们就可以从这个stack中取出一个元素来作为答案

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
using namespace std;
int n;
#define N 100005
const int mod1=1000000009;
const int mod2=998244353;
int fir[2][N],to[2][2*N],nxt[2][2*N],cnt[2];
void adde(int a,int b,int flg)
{
	to[flg][++cnt[flg]]=b;nxt[flg][cnt[flg]]=fir[flg][a];fir[flg][a]=cnt[flg];
	to[flg][++cnt[flg]]=a;nxt[flg][cnt[flg]]=fir[flg][b];fir[flg][b]=cnt[flg];
}
int h[2][N][2],g[2][N][2],siz[2][N],fa[2][N],bas[N][2];
void dfs1(int u,int flg)
{
	siz[flg][u]=1;h[flg][u][0]=h[flg][u][1]=1;
	int v,p;
	for(p=fir[flg][u];p;p=nxt[flg][p]){
		v=to[flg][p];
		if(v!=fa[flg][u]){
			fa[flg][v]=u;
			dfs1(v,flg);
			siz[flg][u]+=siz[flg][v];
			h[flg][u][0]=1ll*(1ll*h[flg][u][0]+1ll*bas[siz[flg][v]][0]*h[flg][v][0]%mod1)%mod1;
			h[flg][u][1]=1ll*(1ll*h[flg][u][1]+1ll*bas[siz[flg][v]][1]*h[flg][v][1]%mod2)%mod2;
		}
	}
}
void dfs2(int u,int flg)
{
	if(u==1){
		g[flg][u][0]=h[flg][u][0];
		g[flg][u][1]=h[flg][u][1];
	}
	int v,p;
	for(p=fir[flg][u];p;p=nxt[flg][p]){
		v=to[flg][p];
		if(v!=fa[flg][u]){
			g[flg][v][0]=1ll*((1ll*g[flg][u][0]-1ll*bas[siz[flg][v]][0]*h[flg][v][0]%mod1+1ll*mod1)%mod1*bas[n-siz[flg][v]][0]%mod1+1ll*h[flg][v][0])%mod1;
			g[flg][v][1]=1ll*((1ll*g[flg][u][1]-1ll*bas[siz[flg][v]][1]*h[flg][v][1]%mod2+1ll*mod2)%mod2*bas[n-siz[flg][v]][1]%mod2+1ll*h[flg][v][1])%mod2;
			dfs2(v,flg);
		}
	}
}
map<pair<int,int>,stack<int> > mp;
int ans[N];
int main()
{
	//freopen("data21.in","r",stdin);
	//freopen("mydata21.out","w",stdout);
	int i,u,v,p=100007;
	bas[0][0]=bas[0][1]=1;
	scanf("%d",&n);
	for(i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		adde(u,v,0);
		bas[i][0]=1ll*bas[i-1][0]*p%mod1;
		bas[i][1]=1ll*bas[i-1][1]*p%mod2;
	}
	dfs1(1,0);dfs2(1,0);
	for(i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		adde(u,v,1);
	}
	dfs1(1,1);dfs2(1,1);
	for(i=1;i<=n;i++)
		mp[make_pair(g[0][i][0],g[0][i][1])].push(i);
	for(i=1;i<=n;i++){
		if(mp.count(make_pair(g[1][i][0],g[1][i][1]))){
			ans[mp[make_pair(g[1][i][0],g[1][i][1])].top()]=i;
			mp[make_pair(g[1][i][0],g[1][i][1])].pop();
		}
		else{
			printf("NO\n");
			return 0;
		}
	}
	printf("YES\n%d",ans[1]);
	for(i=2;i<=n;i++)
		printf(" %d",ans[i]);
	printf("\n");
}

但是这个做法是错的

它虽然对于每个点都找到了哈希值相等的对应点,但是它并没有保证最后答案的树满足A树中边的了解情况

如这组样例

4
2 1
2 3
3 4
3 2
3 1
1 4
 

正确答案应该是

YES
2 3 1 4

而上述程序会输出

YES
4 3 1 2

所以,我们还是要一个dfs来计算答案

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
using namespace std;
int n;
#define N 100005
const int mod1=1000000009;
const int mod2=998244353;
int fir[2][N],to[2][2*N],nxt[2][2*N],cnt[2];
void adde(int a,int b,int flg)
{
	to[flg][++cnt[flg]]=b;nxt[flg][cnt[flg]]=fir[flg][a];fir[flg][a]=cnt[flg];
	to[flg][++cnt[flg]]=a;nxt[flg][cnt[flg]]=fir[flg][b];fir[flg][b]=cnt[flg];
}
int h[2][N][2],g[2][N][2],siz[2][N],fa[2][N],bas[N][2];
void dfs1(int u,int flg)
{
	siz[flg][u]=1;h[flg][u][0]=h[flg][u][1]=1;
	int v,p;
	for(p=fir[flg][u];p;p=nxt[flg][p]){
		v=to[flg][p];
		if(v!=fa[flg][u]){
			fa[flg][v]=u;
			dfs1(v,flg);
			siz[flg][u]+=siz[flg][v];
			h[flg][u][0]=1ll*(1ll*h[flg][u][0]+1ll*bas[siz[flg][v]][0]*h[flg][v][0]%mod1)%mod1;
			h[flg][u][1]=1ll*(1ll*h[flg][u][1]+1ll*bas[siz[flg][v]][1]*h[flg][v][1]%mod2)%mod2;
		}
	}
}
void dfs2(int u,int flg)
{
	if(u==1){
		g[flg][u][0]=h[flg][u][0];
		g[flg][u][1]=h[flg][u][1];
	}
	int v,p;
	for(p=fir[flg][u];p;p=nxt[flg][p]){
		v=to[flg][p];
		if(v!=fa[flg][u]){
			g[flg][v][0]=1ll*((1ll*g[flg][u][0]-1ll*bas[siz[flg][v]][0]*h[flg][v][0]%mod1+1ll*mod1)%mod1*bas[n-siz[flg][v]][0]%mod1+1ll*h[flg][v][0])%mod1;
			g[flg][v][1]=1ll*((1ll*g[flg][u][1]-1ll*bas[siz[flg][v]][1]*h[flg][v][1]%mod2+1ll*mod2)%mod2*bas[n-siz[flg][v]][1]%mod2+1ll*h[flg][v][1])%mod2;
			dfs2(v,flg);
		}
	}
}
map<pair<int,int>,stack<int> > mp;
int ans[N],vis[2][N];
void dfs(int u0,int u1)
{
	ans[u0]=u1;
	vis[0][u0]=1;
	vis[1][u1]=1;
	int v0,p0,v1,p1;
	for(p0=fir[0][u0];p0;p0=nxt[0][p0]){
		v0=to[0][p0];
		if(vis[0][v0]) continue;
		for(p1=fir[1][u1];p1;p1=nxt[1][p1]){
			v1=to[1][p1];
			if(vis[1][v1]) continue;
			if(g[0][v0][0]==g[1][v1][0]&&g[0][v0][1]==g[1][v1][1]){
				dfs(v0,v1);
				break;
			}
		}
	}
}
int main()
{
	//freopen("data21.in","r",stdin);
	//freopen("_mydata21.out","w",stdout);
	int i,u,v,p=100007;
	bas[0][0]=bas[0][1]=1;
	scanf("%d",&n);
	for(i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		adde(u,v,0);
		bas[i][0]=1ll*bas[i-1][0]*p%mod1;
		bas[i][1]=1ll*bas[i-1][1]*p%mod2;
	}
	dfs1(1,0);dfs2(1,0);
	for(i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		adde(u,v,1);
	}
	dfs1(1,1);dfs2(1,1);
	for(i=1;i<=n;i++)
		mp[make_pair(g[0][i][0],g[0][i][1])].push(i);
	int A,B=1;
	for(i=1;i<=n;i++){
		if(mp[make_pair(g[1][i][0],g[1][i][1])].empty()){
			printf("NO\n");
			return 0;
		}
		if(i==1)
			A=mp[make_pair(g[1][i][0],g[1][i][1])].top();
		mp[make_pair(g[1][i][0],g[1][i][1])].pop();
	}
	dfs(A,B);
	printf("YES\n%d",ans[1]);
	for(i=2;i<=n;i++)
		printf(" %d",ans[i]);
	printf("\n");
}

猜你喜欢

转载自blog.csdn.net/C20180602_csq/article/details/102503484