树哈希 学习笔记
树哈希 学习笔记
树哈希,一种判断树是否同构的算法。
令每个节点有一个 \(f_u\) :
\[f_u=1+\prod_{i\in son_u} pri_{sz_i} f_i
\]
我们为了避免卡可以把 \(pri\) random_shuffle
一下
。
然后判断树是否同构,首先节点个数一样,然后重心数量一样。我们可以在重心为根做树哈希。这样判断树是否同构的复杂度为 \(O(n)\)
[BJOI2015] 树的同构
#include <bits/stdc++.h>
const int mod = 998244353, N = 55, INF = 0x3f3f3f3f;
const double eps = 1e-9;
template<typename T>inline T read() { T x = 0; bool f = 0; char ch = getchar(); while (!isdigit(ch)) { f = ch == '-'; ch = getchar(); } while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); } return f ? -x : x; }
template<typename T>inline T max(const T &x, const T &y) { return x > y ? x : y; }
template<typename T>inline T min(const T &x, const T &y) { return x < y ? x : y; }
template<typename T>inline T abs(const T &x) { return x > 0 ? x : -x; }
inline int Mod(int x) { if (x >= mod) { return x - mod; } else if (x < 0) { return x + mod; } else { return x; } }
template<typename T1, typename T2>
struct pair {
T1 first;
T2 second;
pair(const T1 &x = 0, const T2 &y = 0) { first = x; second = y; }
friend pair operator + (const pair<T1, T2> &x, const pair<T1, T2> &y) { return pair(x.first + y.first, x.second + y.second); }
friend pair operator - (const pair<T1, T2> &x, const pair<T1, T2> &y) { return pair(x.first - y.first, x.second - y.second); }
friend bool operator < (const pair<T1, T2> &x, const pair<T1, T2> &y) { return x.first < y.first || (x.first == y.first && x.second < y.second); }
friend bool operator <= (const pair<T1, T2> &x, const pair<T1, T2> &y) { return x.first < y.first || (x.first == y.first && x.second <= y.second); }
friend bool operator > (const pair<T1, T2> &x, const pair<T1, T2> &y) { return x.first > y.first || (x.first == y.first && x.second > y.second); }
friend bool operator >= (const pair<T1, T2> &x, const pair<T1, T2> &y) { return x.first > y.first || (x.first == y.first && x.second >= y.second); }
friend bool operator == (const pair<T1, T2> &x, const pair<T1, T2> &y) { return x.first == y.first && x.second == y.second; }
friend bool operator != (const pair<T1, T2> &x, const pair<T1, T2> &y) { return x.first != y.first || x.second != y.second; }
};
template<typename T1, typename T2>
inline pair<T1, T2> make_pair(const T1 &x, const T2 &y) { return pair<T1, T2>(x, y); }
template<typename T>
struct stack { int top; T vals[N]; bool empty() { return !top; } void push(T x) { vals[++top] = x; } void pop() { if (top > 0) { --top; } return; } void clear() { top = 0; } T TOP() { return top ? vals[top] : T(-1); } };
inline int ksm(int x, int y) { int ret = 1; for ( ; y; y /= 2, x = 1LL * x * x % mod) { if (y & 1) { ret = 1LL * ret * x % mod; } } return ret; }
inline int ksc(int x, int y) { int ret = 0; for ( ; y; y /= 2, x = Mod(x + x)) { if (y & 1) { ret = Mod(ret + x); } } return ret; }
struct graph { int cnt, h[N]; pair<int, int> edge[N * 2]; void add_edge(int x, int y) { edge[cnt].first = y; edge[cnt].second = h[x]; h[x] = cnt++; } void clear() { memset(h, -1, sizeof h); cnt = 0; } };
inline int ls(int k) { return k << 1; }
inline int rs(int k) { return k << 1 | 1; }
inline int sign(double x) { if (abs(x) < eps) { return 0; } else if (x < 0) { return -1; } else { return 1; } }
using std::set;
using std::map;
using std::vector;
using std::ios;
using std::cin;
using std::cout;
using std::endl;
using std::queue;
using std::cerr;
int m, n;
vector<int> pri;
struct Tree {
vector<int> v[N];
int n, rt[3], sz[N], h[3];
void add_edge(int x, int y) {
v[x].push_back(y);
v[y].push_back(x);
}
void find_root(int u, int fa) {
int mx = 0;
sz[u] = 1;
for (auto &j: v[u]) {
if (j != fa) {
find_root(j, u);
sz[u] += sz[j];
mx = max(mx, sz[j]);
}
}
if (max(mx, n - sz[u]) <= n / 2) {
rt[++rt[0]] = u;
}
}
int dfs(int u, int fa) {
int res = 1;
sz[u] = 1;
for (auto &j: v[u]) {
if (j != fa) {
res = 1LL * res * dfs(j, u) % mod * pri[sz[j]] % mod;
sz[u] += sz[j];
}
}
return Mod(res + 1);
}
void solve() {
find_root(1, 0);
for (int i = 1; i <= rt[0]; ++i) {
h[i] = dfs(rt[i], 0);
}
}
} hx[N];
#define orz_1
#define orz_2
int main() {
#ifdef siriehn_nx
freopen("1.in", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0);
cout << std::fixed << std::setprecision(10);
#endif
std::function<bool(int)> check = [&] (int x) {
for (int i = 2; i * i <= x; ++i) {
if (x % i == 0) {
return false;
}
}
return true;
};
int cur = 1;
while (pri.size() <= 100) {
if (check(++cur)) {
pri.push_back(cur);
}
}
std::random_shuffle(pri.begin(), pri.end());
cin >> m;
for (int i = 1; i <= m; ++i) {
cin >> hx[i].n;
for (int j = 1, tmp; j <= hx[i].n; ++j) {
cin >> tmp;
if (tmp) {
hx[i].add_edge(j, tmp);
}
}
hx[i].solve();
int ans = 0;
for (int j = 1; j <= i; ++j) {
if (hx[i].n == hx[j].n && hx[i].rt[0] == hx[j].rt[0]) {
if (hx[i].rt[0] == 1 && hx[i].h[1] == hx[j].h[1]) {
ans = j;
break;
}
else if (hx[i].rt[0] == 2 && (hx[i].h[1] == hx[j].h[1] || hx[i].h[1] == hx[j].h[2])) {
ans = j;
break;
}
}
}
cout << ans << '\n';
}
#ifdef siriehn_nx
cerr << "The time of this EXE is " << (((double) clock()) / CLOCKS_PER_SEC) << "s\n";
#endif
return 0;
}