【洛谷P5043】【模板】树同构

题目

题目链接:https://www.luogu.com.cn/problem/P5043
树是一种很常见的数据结构。
我们把 (N) 个点,(N-1) 条边的连通无向图称为树。
若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。
对于两个树 (T_1)(T_2),如果能够把树 (T_1) 的所有点重新标号,使得树 (T_1) 和树 (T_2) 完全相同,那么这两个树是同构的。也就是说,它们具有相同的形态。
现在,给你 (M) 个无根树,请你把它们按同构关系分成若干个等价类。
(n,mleq 50)

思路

判断树的重构一般都是采用树哈希。
节点 (x) 的哈希值由其儿子求出来。只需要保证相同的树的哈希值一样就可以了。我直接随机了 (50) 个数,然后

[f[x]=sum_{yin m son[x]}f[y] imes a[ m {siz}[y]] ]

最后直接暴力枚举找相同哈希值的就可以了。
时间复杂度 (O(m^2n^2)),实现的精细的话可以做到 (O(nm))

代码

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;

const int N=55;
int Q,n,tot,head[N],siz[N];
ull a[N],f[N],g[N][N];

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

void dfs(int x,int fa)
{
	siz[x]=f[x]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=fa)
		{
			dfs(v,x); siz[x]+=siz[v];
			f[x]+=f[v]*a[siz[v]];
		}
	}
}

int main()
{
	srand('Q'+'u'+'a'+'n'+'t'+'A'+'s'+'k'+'A'+'K'+'I'+'O'+'I');
	for (int i=1;i<=50;i++) a[i]=rand();
	scanf("%d",&Q);
	for (int j=1;j<=Q;j++)
	{
		memset(head,-1,sizeof(head));
		tot=0;
		scanf("%d",&n);
		for (int i=1,x;i<=n;i++)
		{
			scanf("%d",&x);
			if (x) add(x,i),add(i,x);
		}
		for (int i=1;i<=n;i++)
			dfs(i,0),g[j][i]=f[i];
		bool flag=0;
		for (int k=1;k<=j && !flag;k++)
			for (int p=1;p<=50 && !flag;p++)
				for (int q=1;q<=50;q++)
					if (g[k][p] && g[j][q] && g[k][p]==g[j][q])
					{
						cout<<k<<"
"; flag=1;
						break;
					}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/15025242.html