2huk
2024-11-18 14:36:34
求最小的
d ,使得可以在深度为d 的满二叉树上删除若干点后,与给定的树同构。这里删掉点
u 后,u 的所有儿子都会连到u 的父亲上。
显然树形 DP 是必要的。
如果
如果
否则,考虑一个类似合并的过程。我们举几个例子。如果
着重分析一下
(实际上这里省了一步,应该先造深度为
然后变成了
然后变成了
然后是
形式化的,这个过程是
暴力代码是这样的:
void dfs(int u) {
f[u] = 0;
if (g[u].empty()) {
f[u] = 1;
return;
}
if (g[u].size() == 1) {
dfs(g[u][0]);
f[u] = f[g[u][0]] + 1;
return;
}
map<int, int> cnt;
for (int v : g[u]) {
dfs(v);
cnt[f[v]] ++ ;
}
while (cnt.size() != 1 || (*cnt.begin()).second != 1) {
int x = (*cnt.begin()).first;
int y = (*cnt.begin()).second;
cnt[x + 1] += (y + 1) / 2;
cnt.erase(x);
}
f[u] = (*cnt.begin()).first;
}
直接这样交会 TLE on test #62。其实这样复杂度是错误的,因为求一个 DP 值的复杂度是
怎么优化?
仍然考虑这个例子
也就是我们考虑这一个
不难发现这个答案是从
总复杂度
#define tests
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, fa[N];
vector<int> g[N];
int f[N]; // i 的子树,需要几层满二叉树
int work(int x, int y) {
if (!x) return 0;
if (y > 60) return 1;
while (y -- ) x = (x + 1) / 2;
return x;
}
void dfs(int u) {
f[u] = 0;
if (g[u].empty()) {
f[u] = 1;
return;
}
if (g[u].size() == 1) {
dfs(g[u][0]);
f[u] = f[g[u][0]] + 1;
return;
}
map<int, int> cnt;
for (int v : g[u]) {
dfs(v);
cnt[f[v]] ++ ;
}
vector<pair<int, int>> vec;
for (auto e : cnt) vec.push_back(e);
for (int i = 0; i + 1 < vec.size(); ++ i ) {
vec[i + 1].second += work(vec[i].second, vec[i + 1].first - vec[i].first);
}
while (vec.back().second != 1) {
vec.back().first ++ ;
vec.back().second = (vec.back().second + 1) / 2;
}
f[u] = vec.back().first;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; ++ i ) {
g[i].clear();
}
for (int i = 2; i <= n; ++ i ) {
cin >> fa[i];
g[fa[i]].push_back(i);
}
dfs(1);
cout << f[1] - 1 << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1;
#ifdef tests
cin >> T;
#endif
while (T -- ) solve();
return 0;
}