CF1867F Most Different Tree 记录
题目链接:https://codeforces.com/contest/1867/problem/F
题意简述
记 \(P(T)\) 为一棵树 \(T\) 的所有子树的集合。给定一棵 \(n\) 个点的树 \(T\),找出点数相同的树 \(T'\),使 \(P(T')\) 中“与 \(P(T)\) 中至少一棵树同构”的树数量最少。\(n \le 10^6\)。
题解(官解)
这题的解法有一点构造的元素。给出下面的命题:
找到大小最小的(记为 \(sz\))不与 \(P(T)\) 中任何一棵树同构的树 \(H\),则在一个 \(n - sz\) 条边的链的最下端接上 \(H\) 即为一个合法的答案。\(H\) 的任何非自身的子树都与 \(P(T)\) 中至少一棵树同构,\(P(T')\) 中任何其它子树都不与 \(P(T)\) 中任何一棵树同构,因此数量 \(f(T')\) 为 \(sz - 1\)。
下面证明这是最优解。对任何一个最优解 $ T^* $(记 \(f(T^* ) = x\)),找到其中大小最小的“不与 \(P(T)\) 中任何一棵树同构”的子树,则它的大小不超过 \(x + 1\),否则答案会超过 \(x\)。因此按照上面的方法构造的答案不超过 \(x\),因此也是最优解。
因此,问题的关键就在于找到树 \(H\)。大小为 \(n\) 的,固定根无标号树的数量可以在 OEIS 上查到。其中第 \(15\) 项为 \(87811\)。由于 $15 * 87811 > 10^6 $,只要搜索所有大小不超过 \(15\) 的树即可。对是否与 \(P(T)\) 中树同构的判断,可以采用树哈希,先一次遍历计算出 \(P(T)\) 中所有哈希值,之后搜索时即可 \(O(1)\) 判断。
所有大小为 \(n\) 的子树的搜索比较玄学,这里我选择直接枚举根节点的所有子树,可以使用从小到大枚举的方式剪枝。
代码实现(C++)
#include <bits/stdc++.h>
using namespace std;
using i64 = int64_t;
using u64 = uint64_t;
static uint64_t splitmix64(uint64_t x) {
static const uint64_t FIXED_RANDOM =
chrono::steady_clock::now().time_since_epoch().count();
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return FIXED_RANDOM ^ x ^ (x >> 31);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
set<u64> vis;
vector<pair<int, int>> e;
{
vector<vector<int>> adj(n + 1);
for (int ei = 1; ei < n; ei++) {
int u, v;
cin >> u >> v;
e.push_back({u, v});
adj[u].push_back(v);
adj[v].push_back(u);
}
function<u64(int, int)> dfs = [&](int u, int fa) {
u64 h = 1;
for (int v : adj[u]) {
if (v != fa) { h += splitmix64(dfs(v, u)); }
}
vis.insert(h);
return h;
};
dfs(1, -1);
}
vector<vector<int>> sons(0);
vector<u64> h(0);
vector<vector<int>> sz_root(16);
int cnt = 0;
auto print_ans = [&](int sz, int u) {
vector<int> mp(cnt);
for (int i = 1; i <= n - sz; i++) {
cout << i << " " << i + 1 << "\n";
}
int t = n - sz;
function<void(int)> dfs = [&](int u) {
mp[u] = ++t;
for (int v : sons[u]) {
dfs(v);
cout << mp[u] << " " << mp[v] << "\n";
}
};
dfs(u);
exit(0);
};
for (int sz = 1; sz <= n; sz++) {
vector<int> tmpsons;
function<void(int, int, u64)> dfs = [&](int now_sz, int min_add_sz,
u64 now_hash) {
if (now_sz == sz) {
sz_root[sz].push_back(cnt);
cnt++;
sons.push_back(tmpsons);
h.push_back(now_hash);
if (!vis.contains(now_hash)) { print_ans(sz, cnt - 1); }
return;
} else if (now_sz > sz) return;
for (int add_sz = min_add_sz; add_sz + now_sz <= sz; add_sz++) {
for (int v : sz_root[add_sz]) {
tmpsons.push_back(v);
dfs(now_sz + add_sz, add_sz, now_hash + splitmix64(h[v]));
tmpsons.pop_back();
}
}
};
dfs(1, 1, 1);
}
for (auto [u, v] : e)
cout << u << " " << v << "\n";
}
浙公网安备 33010602011771号