1. 程式人生 > 實用技巧 >題解 P5043 【【模板】樹同構([BJOI2015]樹的同構)】

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

聽說題解裡全是 \(O(n^2m)\) 的,今天神 @hehezhou 介紹了一種優秀的方法。

用多項式雜湊,記錄走過的結點的順序。

首先如果是有根樹,而且兒子結點有先後遍歷順序這樣子就是對的。

然後如果兒子結點沒有順序就按照兒子的雜湊值排序,然後再雜湊。

有根樹拓展到無根樹只要找到重心然後再做即可。(兩個重心也是可以的,比較的時候看看兩個雜湊值能否對應上即可)

於是得出了雜湊值,最後暴力比較即可。

時間複雜度 \(\Theta(mn \log n)\), 時間複雜度瓶頸再於對雜湊值排序。

神仙 Forever_Pursuit說他用基數排序,因此是 \(\Theta(mn)\)

程式碼:

#include<bits/stdc++.h>
#define L(i, j, k) for(int i = j, i##E = k; i <= i##E; i++) 
#define R(i, j, k) for(int i = j, i##E = k; i >= i##E; i--)
#define ll long long
#define ull unsigned long long 
#define db double
#define pii pair<int, int>
#define pil pair<int, lonf long>
#define mkp make_pair
using namespace std;
const int N = 55;
const int mod = 1019260817;
const int G = 19491001;
int Pow[N];
int n, m;
struct Tree {
	int A, B;
} f[N];
bool operator == (Tree aa, Tree bb) {
	return aa.A == bb.A && aa.B == bb.B;
}
int head[N], edge_id;
struct edge {
	int to, next;
} e[N << 1];
void add_edge(int u, int v) {
	++edge_id, e[edge_id].to = v, e[edge_id].next = head[u], head[u] = edge_id;
}
int has[N], siz[N], dep[N], rt, rrt, rtm;
void findrt(int x, int fa) {
	siz[x] = 1;
	int maxn = 0;
	for(int i = head[x]; i; i = e[i].next) {
		int v = e[i].to;
		if(v == fa) continue;
		findrt(v, x), siz[x] += siz[v], maxn = max(maxn, siz[v]);
	}
	maxn = max(maxn, n - siz[x]);
	if(maxn < rtm) rtm = maxn, rt = x, rrt = 0;
	else if(maxn == rtm) rrt = x;
}
int tot;
pii sav[N];
void dfs(int x, int fa) {
	has[x] = 1ll * dep[x] * Pow[1] % mod, siz[x] = 1;
	for(int i = head[x]; i; i = e[i].next) {
		int v = e[i].to;
		if(v == fa) continue;
		dep[v] = dep[x] + 1, dfs(v, x);
	}
	tot = 0;
	for(int i = head[x]; i; i = e[i].next) {
		int v = e[i].to;
		if(v == fa) continue;
		sav[++tot] = mkp(has[v], siz[v]);
	}
	sort(sav + 1, sav + tot + 1);
	L(i, 1, tot) (has[x] += 1ll * sav[i].first * Pow[siz[x]] % mod) %= mod, siz[x] += sav[i].second;
}
void In(int x) {
	rtm = mod, rrt = 0;
	scanf("%d", &n);
	L(i, 1, n) {
		int v; scanf("%d", &v);
		if(v) add_edge(i, v), add_edge(v, i);
	}
	findrt(1, -1);
	dep[rt] = 1, dfs(rt, -1), f[x].A = has[rt];
	if(rrt) dep[rrt] = 1, dfs(rrt, -1), f[x].B = has[rrt];
	if(f[x].A < f[x].B) swap(f[x].A, f[x].B);
	L(i, 1, n) head[i] = 0;
	edge_id = 0;
}
int mian() {
	Pow[0] = 1;
	L(i, 1, 50) Pow[i] = 1ll * Pow[i - 1] * G % mod;
	scanf("%d", &m);
	L(i, 1, m) In(i);
	L(i, 1, m) L(j, 1, i) if(f[i] == f[j]) {
		printf("%d\n", j);
		break;
	}
	return 0;
}

祝大家學習愉快!