题目
题目链接: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\) 个数,然后
最后直接暴力枚举找相同哈希值的就可以了。
时间复杂度 \(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;
}