【題解】【模板】樹同構([BJOI2015]樹的同構)

【模板】樹同構([BJOI2015]樹的同構)

\(\text{Solution:}\)

由於月賽有一個和樹同構相關的題目,所以來學一下樹同構。

這裏不用樹哈希的做法,考慮用括號序列。

我們發現:當進入一個點的時候記錄一個左括號,出去的時候記錄一個右括號,這樣會形成一個長度爲 \(2n\) 的括號序列。

同時,在沒有標號的情況下,這種括號序列是可以確定樹的結構的。

但是我們觀察到,當我們更改子樹的拼接順序,那麼其括號序列就會改變。

所以我們考慮求字典序最小的括號序列。容易證明兩棵樹同構當且僅當兩棵樹對應的最小括號序列相同。

於是我們考慮直接暴力求這個東西。先把子樹的最小表示求出來,然後暴力排序。複雜度 \(O(n^2)\) 因爲排序的複雜度與 string 拼接的複雜度相比並不高。

然後考慮無根樹怎麼比較。考慮轉化爲有根樹,這樣就可以直接用重心來做了,由於最多兩個重心,這樣就可以只比較兩個點的最小表示。複雜度就是 \(O(n^2\times m)\) 的了。

代碼裏面封裝了一個對樹求最小表示的板子。

#include<bits/stdc++.h>
using namespace std;
typedef double db;
//#define int long long
const int mod=1e9+7;
const db eps=1e-10;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
inline db Max(db x,db y){return x-y>eps?x:y;}
inline db Min(db x,db y){return x-y<eps?x:y;}
inline int Add(int x,int y,int M=mod){return (x+y)%M;}
inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
inline int Abs(int x){return x<0?-x:x;}
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
	return s*w;
}
inline void write(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline int qpow(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=Mul(res,x);
		x=Mul(x,x);y>>=1;
	}
	return res;
}
typedef pair<int,int> pr;
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
#define Bt(a) bitset<a>
const int N=200;
struct Tree_Construction{
	int head[N],tot,n,siz[N],mx[N],Mi;
	struct E{int nxt,to;}e[N];
	string f[N],g[N],tmp;
	inline void link(int x,int y){
		e[++tot]=(E){head[x],y};
		head[x]=tot;
	}
	void dfs(int x,int fa){
		siz[x]=1;
		for(int i=head[x];i;i=e[i].nxt){
			int j=e[i].to;
			if(j==fa)continue;
			dfs(j,x);
			siz[x]+=siz[j];
			mx[x]=Max(mx[x],siz[j]);
		}
		mx[x]=Max(mx[x],n-siz[x]);
		Mi=Min(Mi,mx[x]);
	}
	void Show(int x,int fa){
		f[x]="0";
		for(int i=head[x];i;i=e[i].nxt){
			int j=e[i].to;
			if(j==fa)continue;
			Show(j,x);
		}
		int cnt=0;
		for(int i=head[x];i;i=e[i].nxt){
			int j=e[i].to;
			if(j==fa)continue;
			g[++cnt]=f[j];
		}
		sort(g+1,g+cnt+1);
		for(int i=1;i<=cnt;++i)f[x]+=g[i];
		f[x]+="1";
	}
	void Init(){
		n=read();
		for(int i=1;i<=n;++i){
			int u=read();
			if(!u)continue;
			link(i,u);link(u,i);
		}
	}
	string Get(){
		Init();
		Mi=n+1;
		dfs(1,0);
		tmp="1";
		for(int i=1;i<=n;++i){
			if(mx[i]==Mi){
				Show(i,0);
				tmp=min(tmp,f[i]);
			}
		}
		return tmp;
	}
}tr[N];
int T;
string mn[N];
int main(){
	T=read();
	for(int i=1;i<=T;++i){
		mn[i]=tr[i].Get();
		for(int j=1;j<=i;++j){
			if(mn[j]==mn[i]){
				printf("%d\n",j);
				break;
			}
		}
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章