BJOI2015 树的同构
Description
树是一种很常见的数据结构。
我们把N个点,N-1条边的连通无向图称为树。
若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。
对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相
同,那么这两个树是同构的。也就是说,它们具有相同的形态。
现在,给你M个有根树,请你把它们按同构关系分成若干个等价类。
Input
第一行,一个整数M。
接下来M行,每行包含若干个整数,表示一个树。第一个整数N表示点数。接下来N
个整数,依次表示编号为1到N的每个点的父亲结点的编号。根节点父亲结点编号为0。
Output
输出M行,每行一个整数,表示与每个树同构的树的最小编号。
Sample Input
4
4 0 1 1 2
4 2 0 2 3
4 0 1 1 1
4 0 1 2 3
Sample Output
1
1
3
1
HINT
【样例解释】
编号为1, 2, 4 的树是同构的。编号为3 的树只与它自身同构。
100% 的数据中,1 ≤ N, M ≤ 50。
分析
在讲这道题之前,我们先来了解一下括号序列
考虑 dfs 序,在 dfs 完一个点的时候,我们同样把他加入序列,这样得
到的序列叫做括号序列。
对于一棵有根树,如果子节点有序,那么这棵树唯一对应着一个括号序
列。
显然,对于这道题,给出的每一棵树,如果我们把不同的节点作为根,往下dfs,那么就算这两棵树的形态一样,我们得到的括号序列也不相同
退一步,就算我们从同一个节点出发,如果dfs的顺序不一样,最后得到的括号序列也不相同
这提示我们本题的关键(判断两棵树是否同构)就在于选对根和遍历顺序
又因为对于一个括号序列,我们发现,每一个大括号里面包含的次级括号表示的是它子树的遍历情况,而且整体交换次级括号的顺序不会影响树的形态,所以我们考虑将每个子树遍历得到的括号序列排个序,这就是伪的最小表示法,现在遍历顺序解决了,考虑根。
我们很容易想到,一棵树的重心最多只有两个,而两棵树如果同构则其重心肯定是同一(两)个。但由于这道题的范围只有50,听说有人直接枚举,将所有的点都作为根,来遍历一遍,都可以卡过去
代码
#include<bits/stdc++.h>
#define N 100
using namespace std;
int m,n,maxn;
int sze[N],son[N];
int nxt[N<<1],head[N],to[N<<1],cnt=0;
string ans[N<<1],h[N<<1];
void add(int x,int y){
nxt[++cnt]=head[x];head[x]=cnt;to[cnt]=y;
nxt[++cnt]=head[y];head[y]=cnt;to[cnt]=x;
}
void getG(int u,int fu){
sze[u]=1;son[u]=0;
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(v==fu) continue;
getG(v,u);
sze[u]+=sze[v];
if(sze[v]>son[u]) son[u]=sze[v];
}
son[u]=max(son[u],n-sze[u]);
maxn=min(maxn,son[u]);
}
void dfs(int u,int fu){
h[u]="(";
vector<string> V;
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(v==fu) continue;
dfs(v,u);
V.push_back(h[v]);
}
sort(V.begin(),V.end());
for(vector<string> ::iterator it=V.begin();it!=V.end();++it){
h[u]+=*it;
}
h[u]+=")";
}
int main(){
scanf("%d",&m);
int i,j,t;
for(t=1;t<=m;++t){
scanf("%d",&n);
cnt=0;memset(head,0,sizeof(head));
for(i=1;i<=n;++i){
scanf("%d",&j);
if(j) add(i,j);
}
maxn=n;
getG(1,0);
string tmp=" ";
for(i=1;i<=n;++i){
if(son[i]==maxn){
dfs(i,0);
if(h[i]<tmp||tmp==" ") tmp=h[i];
}
}
ans[t]=tmp;
for(i=1;i<=t;++i){
if(ans[i]==ans[t]) {
printf("%d\n",i);
break;
}
}
}
return 0;
}