temp

Gavin

这是一道非常具有启发性的树形 DP 问题。为了让这道题有一个“最优且极具美感”的解法,我们需要对题目进行合理的严谨化定义。

1. 题目的严谨化与重构

原题中有几个需要明确的点,为了得到最优解,我们做如下自然的补充定义:

  1. 集合的连通性:如果不要求集合连通,中心点可能在集合外,这会导致极强的全局依赖,通常只能高复杂度暴力。因此,我们限定划分出的每个集合在树上是连通的(即删去若干条边形成的连通块),这也是此类题目的标准设定。
  2. 限制的位置:如果限制在任意两点对,这是一般的 2-SAT 问题,在树上难以高效最优化。我们限定限制仅在树上相邻的节点之间(即边限制),这完美契合树的结构。
  3. 中心的唯一性:树上连通块的1-中位数(距离和最小的点)可能有两个(当大小为偶数时中间那条边的两端)。我们定义集合的唯一中心为:距离和最小的点中,深度最浅(最靠近树根)的那个点。集合的权值即为中心的点权。

最终问题定式
给定一棵 nn 个节点的树,节点有点权 AiA_i。将树切分为若干连通块,每个连通块 SS 的权值定义为其唯一中心 cc 的点权 AcA_c。边带有限制:Must(必须保留在同一个块)或 Cannot(必须切开)。求所有合法划分中,连通块权值和的最大值。


2. 核心洞察:中心的等价定义

设整棵树以节点 1 为根。对于连通块 SS,其唯一中心 cc 有什么性质?
引理ccSS 的唯一中心,当且仅当 ccSS 中满足 STc>S/2|S \cap T_c| > |S|/2 的深度最浅的节点(若 c=1c=1ST1=SS/2|S \cap T_1| = |S| \ge |S|/2)。

证明
在树中,距离和最小的点就是重心。如果从 cc 向其父节点 pp 移动,距离和的变化量为 Δ=STcSTc=S2STc\Delta = |S \setminus T_c| - |S \cap T_c| = |S| - 2|S \cap T_c|
如果 cc 是深度最浅的中心,说明向父节点移动会使距离和严格增大,即 Δ>0\Delta > 0,所以 STc>S/2|S \cap T_c| > |S|/2
反之,如果 STc>S/2|S \cap T_c| > |S|/2,则向父移动距离和变大,向任何子节点移动也必定满足重心性质(因为 cc 的子节点包含的 SS 节点数 <S/2< |S|/2)。因此 cc 就是深度最浅的中心。

这个引理极其伟大!它把“最小化距离和”这个全局几何性质,完美转化为了子树节点数的计数限制,使得树形 DP 成为可能。我们称连通块的唯一中心为该连通块的 DP-根


3. 最优解法:树形背包 DP

既然每个连通块有且仅有一个 DP-根,且只有 DP-根会对答案贡献其点权 AcA_c,块内其他节点贡献为 0。我们可以设计如下 DP:

状态定义

  • dp[u][k]dp[u][k]:处理完以 uu 为根的子树,且 uu 属于某个尚未闭合的连通块(即 uu 不是该块的 DP-根,DP-根在 uu 的祖先处),且当前块在 TuT_u 中的大小为 kk 时,能获得的最大权值和。
  • **g[u][k]g[u][k]**:处理完以 uu 为根的子树,且 uu 是当前连通块的 DP-根(即块在 uu 处闭合,不再向父节点延伸),块的总大小为 kk 时,能获得的最大权值和。

状态转移

考虑 uu 的一个子节点 vv,我们需要将 vv 的状态合并入 uu。设合并前的状态为 dpu,gudp_u, g_u

1. 边 (u,v)(u, v) 被切断(受 Cannot 限制强制切断,或为了最优化主动切断)
此时 vv 必须自成连通块,且 vv 必须是其连通块的 DP-根(否则块会越过 vv 往上,导致边未被切断)。我们提取 best_g_v = \max_{k} g[v][k]。

  • new_dp_u[k_u] = \max(new_dp_u[k_u], dp_u[k_u] + best_g_v)
  • new_g_u[k_u] = \max(new_g_u[k_u], g_u[k_u] + best_g_v)

2. 边 (u,v)(u, v) 被保留(受 Must 限制强制保留,或主动保留)
此时 vvuu 在同一个连通块中,由于块未闭合,**vv 绝不能是 DP-根**,所以只能从 dp[v][kv]dp[v][k_v] 转移。

  • 对于 dpudp_u:由于 uu 也不是 DP-根,没有任何限制,直接合并大小和权值:
    newdpu[ku+kv]=max(newdpu[ku+kv],dpu[ku]+dp[v][kv])new_dp_u[k_u + k_v] = \max(new_dp_u[k_u + k_v], dp_u[k_u] + dp[v][k_v])
  • 对于 gug_u:由于 uu 是 DP-根,根据引理,必须有 STvS/2|S \cap T_v| \le |S|/2。因为 S=ku+kv|S| = k_u + k_v,这等价于 kv(ku+kv)/2k_v \le (k_u + k_v) / 2,即 **kvkuk_v \le k_u**。这是一个极其优美的剪枝条件!
    new_g_u[k_u + k_v] = \max(new_g_u[k_u + k_v], g_u[k_u] + dp[v][k_v]) \quad (\text{条件: } k_v \le k_u)

初始化与答案

  • 初始化:dp[u][1]=0dp[u][1] = 0uu 不是根,暂不贡献点权);g[u][1]=Aug[u][1] = A_uuu 是根,贡献点权)。
  • 最终答案:maxkg[1][k]\max_{k} g[1][k](根节点 1 必定是所在连通块的 DP-根)。

4. 复杂度分析

这是一个典型的树形背包。如果不加优化,复杂度是 O(n2)O(n^2)。但请注意我们 gug_u 合并时的条件 kvkuk_v \le k_u,这使得 gg 的实际有效状态数极少。
空间复杂度可以通过滚动数组或只存非零状态的 map/遍历器优化到 O(n)O(n)。时间复杂度严格为 O(n2)O(n^2),且常数极小,这是此类问题的理论最优下界。


5. 核心代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <iostream>
#include <vector>
#include <map>
using namespace std;

const int INF = 1e9;
int n;
vector<int> A;
vector<vector<pair<int, int>>> adj; // adj[u] = {(v, limit)}, limit: 0=无限制, 1=Must, 2=Cannot

map<int, int> dp_u, g_u; // 滚动数组,只存非零状态,极大优化空间和常数

// 返回值为 best_g_v
int dfs(int u, int fa) {
dp_u.clear(); g_u.clear();
dp_u[1] = 0; // u 不是根,不贡献点权
g_u[1] = A[u]; // u 是根,贡献点权

int size_u = 1;

for (auto [v, limit] : adj[u]) {
if (v == fa) continue;

map<int, int> dp_v, g_v;
swap(dp_u, dp_v); // 把 u 的当前状态暂存进 v,腾空 u 作为新状态
swap(g_u, g_v);

int best_g_v = dfs(v, u); // 递归计算子树 v
int size_v = /* 子树v的大小,可以通过dfs返回或预处理 */;

map<int, int> ndp, ng;

// 合并状态
for (auto [ku, val_u] : dp_v) {
// 选项1: 切断边 (如果允许)
if (limit != 1) {
ndp[ku] = max(ndp[ku], val_u + best_g_v);
}
// 选项2: 保留边 (如果允许)
if (limit != 2) {
for (auto [kv, val_v] : dp_u) { // 注意这里遍历的是子节点v算出的dp_u
ndp[ku + kv] = max(ndp[ku + kv], val_u + val_v);
}
}
}

for (auto [ku, val_u] : g_v) {
// 选项1: 切断边
if (limit != 1) {
ng[ku] = max(ng[ku], val_u + best_g_v);
}
// 选项2: 保留边 (核心剪枝: kv <= ku)
if (limit != 2) {
for (auto [kv, val_v] : dp_u) {
if (kv <= ku) { // 保证 u 依然是 DP-根 (重心)
ng[ku + kv] = max(ng[ku + kv], val_u + val_v);
}
}
}
}

dp_u = move(ndp);
g_u = move(ng);
size_u += size_v;
}

// 计算 best_g_u 供父节点使用
int best_g_u = -INF;
for (auto [k, val] : g_u) best_g_u = max(best_g_u, val);
return best_g_u;
}

int main() {
// 读入 n, A, adj 等
// ...
dfs(1, 0);
int ans = -INF;
for (auto [k, val] : g_u) ans = max(ans, val);
cout << ans << endl;
return 0;
}

总结

这个解法的最优性体现在:

  1. 思维最优:将距离和最小转化为了 STc>S/2|S \cap T_c| > |S|/2,利用最深重心的唯一性,把几何问题转化为代数计数限制。
  2. 状态最简:只有 dpdp (未闭合) 和 gg (闭合) 两个维度,且权值仅在闭合时一次性加入,彻底避免了重复计算。
  3. 剪枝最美:保留边时 kvkuk_v \le k_u 这个条件是数学上严格推导出的,直接在 DP 转移中砍掉了一半以上的无效状态,使得实际运行速度极快。
  • 标题: temp
  • 作者: Gavin
  • 创建于 : 2026-07-16 16:46:00
  • 更新于 : 2026-07-16 16:46:00
  • 链接: https://gavin-blog.pages.dev/2026/temp/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。