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\%\) 的数据,\(1\leq N,M\leq 50\)。
树的同构问题,用到树哈希,也就是把树哈希为一个数值,从而可以进行比较。
\(f[u]\):以u点为根的子树的哈希值。
其中,\(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\n",da[i]);
return 0;
}