1. 程式人生 > >BZOJ4337 樹的同構(樹的重心+括號序列/雜湊)

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;
}