BZOJ4337 树的同构(树的重心+括号序列/哈希)

【题目描述】

树是一种很常见的数据结构。

我们把N个点,N-1条边的连通无向图称为树。

若将某个点作为根,从根开始遍历,则其它的点都有一个前驱,这个树就成为有根树。

对于两个树T1和T2,如果能够把树T1的所有点重新标号,使得树T1和树T2完全相

同,那么这两个树是同构的。也就是说,它们具有相同的形态。

现在,给你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

【备注】

100% 的数据中,1 ≤ N, M ≤ 50。 

【题目分析】

显然,这个题可以dfs求每棵树的哈希然后判断,这里有一种更高级的做法,就是利用dfs序的改进版——括号序列。

因为这是无根树,又因为一棵树的重心最多只有两个,所以我们就以重心为树的根,这样树高就有了保证,如果有两个重心就跑两遍,再按照字典序排序取字典序较小的那个。

【代码~】

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int MAXN=1e5+10;
const int INF=0x3f3f3f3f;

int Read()
{
	int i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

int n,minn,m,cnt;
int siz[MAXN],head[MAXN],maxx[MAXN];
int to[MAXN],nxt[MAXN];
string hash[MAXN],p[MAXN],ans[MAXN];

void add(int x,int y)
{
	cnt++;
	nxt[cnt]=head[x];
	head[x]=cnt;
	to[cnt]=y;
}

void dfs(int u,int fa)
{
	int sum=0;
	for(int i=head[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(v!=fa)
		  dfs(v,u);
	}
	for(int i=head[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(v!=fa)
		{
			p[++sum]=hash[v];
		}
	}
	hash[u]='(';
	sort(p+1,p+sum+1);
	for(int i=1;i<=sum;++i)
	  hash[u]+=p[i];
	hash[u]+=')';
}

void getroot(int u,int fa)
{
	maxx[u]=0,siz[u]=1;
	for(int i=head[u];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(v!=fa)
		{
			getroot(v,u);
			siz[u]+=siz[v];
			maxx[u]=max(maxx[u],siz[v]);
		}
	}
	maxx[u]=max(maxx[u],m-siz[u]);
	minn=min(minn,maxx[u]);
}

string getans()
{
	string ret="";
	m=Read();
	memset(head,-1,sizeof(head));
	for(int i=1;i<=m;++i)
	{
		int x=Read();
		if(x)
		{
			add(x,i);
			add(i,x);
		}
	}
	minn=1<<30,getroot(1,0);
	for(int i=1;i<=m;++i)
	  if(maxx[i]==minn)
	  {
	  	  dfs(i,0);
	  	  ret=max(ret,hash[i]);
	  }
	return ret;
}

int main()
{
	n=Read();
	for(int i=1;i<=n;++i)
	  ans[i]=getans();
	int j;
	for(int i=1;i<=n;++i)
	{
		for(j=1;j<i;++j)
		  if(ans[i]==ans[j])
		    break;
		printf("%d\n",j);
	}
	return 0;
}

 

posted @ 2018-10-06 18:49  Ishtar~  阅读(195)  评论(0编辑  收藏  举报