树上背包时间复杂度证明
P2014 [CTSC1997] 选课
本文证明树上背包的时间复杂度是
首先给出代码实现:
#include <bits/stdc++.h>
using namespace std;
#define dbg(...) cerr << "[" << #__VA_ARGS__ << "] = ", debug_out(__VA_ARGS__)
template <typename T> void debug_out(T t) { cerr << t << endl; }
template <typename T, typename... Ts> void debug_out(T t, Ts... ts) {
cerr << t << ", ";
debug_out(ts...);
}
template <class F> struct y_combinator {
F f;
template <class... Args> decltype(auto) operator()(Args &&...args) {
return f(*this, std::forward<Args>(args)...);
}
};
template <class F> auto make_y_combinator(F f) { return y_combinator<F>{f}; }
int main() {
cin.tie(0)->sync_with_stdio(0);
int n, m;
cin >> n >> m;
vector<int> a(n + 1);
vector<vector<int>> e(n + 1);
for (int i = 1, f; i <= n; i++) {
cin >> f >> a[i];
e[f].emplace_back(i);
}
++m;
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
vector<int> sz(n + 1);
auto dfs = make_y_combinator([&](auto self, int u) -> void {
dp[u][1] = a[u];
sz[u] = 1;
for (int v : e[u]) {
self(v);
for (int i = min(sz[u], m); i >= 1; i--) {
for (int j = min(sz[v], m - i); j >= 1; j--) {
dp[u][i + j] = max(dp[u][i + j], dp[u][i] + dp[v][j]);
}
}
sz[u] += sz[v];
}
});
dfs(0);
cout << dp[0][m] << endl;
return 0;
}
让我们重点关注:
for (int i = min(sz[u], m); i >= 1; i--) {
for (int j = min(sz[v], m - i); j >= 1; j--) {
dp[u][i + j] = max(dp[u][i + j], dp[u][i] + dp[v][j]);
}
}
这也就是说,对于每条边
其中:
那么总时间复杂度即为
接下来存在两个 自然的观察:
-
考虑
\min(m, pre_v) \times \min(m, siz_v) \le m^2 ,由于边数O(n) ,因此总时间复杂度不超过\mathcal O(nm^2) 。 -
考虑
\min(m, pre_v) \times \min(m, siz_v) \le pre_v \cdot siz_v ,这可以理解成在u \to v 处对所有满足如下条件的(x, y) 进行计数:注意到任意
(x,y) 仅会在它们的\text{lca} 处被计数一次,于是有\sum_{u\to v} pre_v \cdot siz_v \le n^2 ,因此总时间复杂度不超过\mathcal O(n^2) 。 -
然未尽其析。
下证时间复杂度为
我们将子树大小
同时把边分为三类:
- 蓝边:连接两个蓝点的边
- 黄边:连接红点和蓝点的边
- 红边:连接两个红点的边
例如
接下来我们分四类讨论:
考虑所有蓝边,我们可以得到若干 极大蓝子树(图中有蓝点 6, 7, 9, 10, 11, 18, 19, 20 的子树)。根据上述 自然的观察 2,大小为
-
- 由于 极大蓝子树 是互斥的,有
\sum s_i \le n 。
这可以推出
于是所有蓝边对时间复杂度的贡献不超过
考虑所有黄边
于是所有黄边对时间复杂度的贡献不超过
注意到仅红点和红边也构成一棵树,我们称之为红树。
考虑 红树的叶子节点,其个数为
仅考虑满足如下条件的红边
(图中有 深红边
可以将每个 深红点 理解为,对至少两个 红树的叶子节点 进行合并。因此 深红点 的个数
根据上述 自然的观察 1,每条边对时间复杂度的贡献不超过
最后考虑 浅红边
于是所有 浅红边 对时间复杂度的贡献不超过
综上,树上背包时间复杂度不超过
然犹未尽析。
有没有更直观一点的解释呢?
回到式子:
考虑 dfs 序
-
x$ 在 **$v$ 左边的子树** 中,且 $dfn_x \ge dfn_v - m -
y$ 在 **$v$ 的子树** 中,且 $dfn_y < dfn_v + m
注意到任意