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

题目

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

思路

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

\[f[x]=\sum_{y\in \rm son[x]}f[y]\times a[\rm {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<<"\n"; flag=1;
						break;
					}
	}
	return 0;
}
posted @ 2021-07-17 21:55  stoorz  阅读(116)  评论(0编辑  收藏  举报