luogu5043

题目描述

树是一种很常见的数据结构。

我们把 N 个点,N−1 条边的连通无向图称为树。

若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。

对于两个树 (T_1)(T_2),如果能够把树 (T_1)的所有点重新标号,使得树 (T_1)和树 (T_2)完全相同,那么这两个树是同构的。也就是说,它们具有相同的形态。

现在,给你 M 个无根树,请你把它们按同构关系分成若干个等价类。

输入格式

第一行,一个整数 M。

接下来 M 行,每行包含若干个整数,表示一个树。第一个整数 N表示点数。接下来 N 个整数,依次表示编号为 1 到 N 的每个点的父亲结点的编号。根节点父亲结点编号为 0。

输出格式

输出 M 行,每行一个整数,表示与每个树同构的树的最小编号。

输入输出样例

输入

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

输出

1 
1 
3 
1 

说明/提示
编号为 1, 2, 4的树是同构的。编号为 3 的树只与它自身同构。

对于 (100\%) 的数据,(1leq N,Mleq 50)


树的同构问题,用到树哈希,也就是把树哈希为一个数值,从而可以进行比较。
(f[u]):以u点为根的子树的哈希值。

[f[u]=sum_{v in son_u}f[v] imes prime[siz[v]] ]

其中,(siz[v])表示以v为根的子树的大小,而(prime[x])表示很x个素数

在这个题中,首先求了(f[]),但由于是无根树,所以以通过getg()函数求了(g[])表示以每一个点为根时,这棵树的哈希值。
两棵树要同构,那么它们分别以某一点为根时,他们的哈希值一定相等。应该这样说,如果两棵树同构,那么,A树中每一个点为根,B树中必然最少有一个点为根的树的哈希值与它相同。所以我们只求出两个树中哈希值最小的值,如果他们相等,他们就是同构的,否则不可以同构。


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=55;
struct edge
{
	int u,v,nxt;
}e[maxn<<1];
int head[maxn],js;
void addage(int u,int v)
{
	e[++js].u=u;e[js].v=v;
	e[js].nxt=head[u];head[u]=js;
}
int n,m;
bool isprime[1000];
int prime[maxn];
void getprime()
{
	int cnt=0;
	for(int i=2;i<1000;++i)
	{
		if(cnt>=54)break;
		if(isprime[i]==0)prime[++cnt]=i;
		for(int j=1;j<=cnt;++j)
		{
			if(i*prime[j]>=1000)break;
			isprime[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
}
int f[maxn],siz[maxn];
void dfs(int u,int fa)
{
	siz[u]=f[u]=1;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].v;
		if(v==fa)continue;
		dfs(v,u);
		siz[u]+=siz[v];
		f[u]+=f[v]*prime[siz[v]];
	}
}
int g[maxn];
void getg(int u,int fa,int faf)
//faf:以u点向父亲方向(向上)的子树的f[]
{
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].v;
		if(v==fa)continue;
		int tp=f[u]-f[v]*prime[siz[v]]+faf*prime[n-siz[u]];
		//tp为v点向父亲方向上的hash值
		g[v]=f[v]+tp*prime[n-siz[v]];
		getg(v,u,tp);
	}
}
struct node{
	int gg,no,siz;
}sz[maxn];
bool cmp (node a,node b)
{
	if(a.siz<b.siz)return 1;
	if(a.siz==b.siz && a.gg<b.gg)return 1;
	if(a.siz==b.siz && a.gg==b.gg && a.no<b.no)return 1;
	return 0;
}
int da[maxn];
int main()
{
	scanf("%d",&m);
	getprime();
	
	for(int i=1;i<=m;++i)
	{
		scanf("%d",&n);
		js=0;memset(head,0,sizeof head);
		for(int v,j=1;j<=n;++j)
		{
			scanf("%d",&v);
			if(v==0)continue;
			addage(j,v);addage(v,j);
		}
		memset(f,0,sizeof f);
		memset(g,0,sizeof g);
		dfs(1,0);
		g[1]=f[1];
		getg(1,0,0);
		int mn=0x7fffffff;
		sz[i].siz=n;sz[i].no=i;
		for(int j=1;j<=n;++j)
		{
			if(g[j]<mn)
			{
				mn=g[j];
				sz[i].gg=g[j];
			}
		}
	}
	sort(sz+1,sz+1+m,cmp);
	for(int i=1;i<=m;++i)
		if(sz[i].siz==sz[i-1].siz && sz[i].gg==sz[i-1].gg)da[sz[i].no]=da[sz[i-1].no];
		else
			da[sz[i].no]=sz[i].no;
	for(int i=1;i<=m;++i)printf("%d
",da[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/gryzy/p/15251547.html