[ABC311Ex] Many Illumination Plans 题解

Description

给定一棵根节点为 11 的有根树 TT,树中共有 NN 个节点,编号从 11NN。对于每个顶点 ii2iN2 \leq i \leq N),其父节点是 PiP_i。每个节点都有两个非负整数属性,分别称为美丽值重量。节点 ii 的美丽值为 BiB_i,重量为 WiW_i。此外,节点被涂上红色或蓝色,其颜色用整数 CiC_i 表示:当 Ci=0C_i=0 时,节点 ii 为红色;当 Ci=1C_i=1 时,为蓝色。

对于每一个节点 vv,定义函数 F(v)F(v) 表示以下问题的解:

从树 TT 中提取出以 vv 为根的子树,称之为 UU。对 UU 可以进行若干次以下操作(操作不会改变未删除节点的属性):

  • 选择一个非根节点 cc,设 cc 的父节点为 pp
  • 对于所有与 cc 相连的边:
    • 假设边的另一端是 uu,删除这条边,然后用一条以 pp 为父节点的新边连接 ppuu
  • 删除节点 cc 和边 p,cp,c

最终的 UU 满足以下条件即为好的有根树

  • UU 中相连节点的颜色不同。
  • 所有节点的重量总和不超过 XX

找出在所有可能的好的有根树中,节点美丽值总和的最大值。

请输出每个节点 vv 对应的 F(v)F(v),即 F(1),F(2),,F(N)F(1), F(2), \dots, F(N)

2N200,0X500002\leq N\leq 200,0\leq X\leq 50000

Solution

首先这题有个很简单的想法是设 fu,s,0/1f_{u,s,0/1} 表示 uu 的子树,选的总重量为 ss,最上面的那些点的颜色为 0/10/1 的最大美丽度之和,暴力转移就是 O(nX2)O(nX^2)

注意到如果按照上面的做法从下往上转移,则无法规避掉单次 O(X2)O(X^2)min\min 加卷积,所以考虑从上往下 dp,将 dp 数组合并改为往 dp 中插入元素。

先考虑固定子树的根怎么做。

对于当前的点 uu,由于儿子之间是平等的,所以有个很巧妙的想法是往 dfs 的状态里加入一个数组 ff,表示这个子树里面的选择需要在 ff 的基础上更新,然后 dfs 的返回结果改成两个大小为 x+1x+1 的数组,分别表示 uu 的子树内选的最浅的点颜色为 0/10/1,且在 ff 的基础上更新的结果数组。

由于 uu 的儿子中有一个儿子可以只遍历一次,其余都需要两次,容易想到先遍历重儿子,得到只有重儿子子树的 f0f_0f1f_1。然后对于其它儿子 vv,分别调用 f0dfs(v,f0)[0],f1dfs(v,f1)[1]f_0\leftarrow \text{dfs}(v,f_0)[0],f_1\leftarrow\text{dfs}(v,f_1)[1]

这个东西的复杂度是 T(szu)=T(szsonu)+2vsonuT(szv)+O(X)3T(szu/2)+O(X)=O(nlog23X)=O(n1.59X)\displaystyle T(sz_u)=T(sz_{son_u})+2\sum_{v\neq son_u}{T(sz_v)}+O(X)\leq 3\cdot T(sz_u/2)+O(X)=O(n^{\log_23}X)=O(n^{1.59}X)

暴力对于每个子树做这件事情就是 O(n2.59X)O(n^{2.59}X),过不了。


考虑优化。

可以感受到上面的做法还是有些浪费,因为如果要求以 uu 为根的答案,这个需要用到 sonuson_u 从零开始的 dp 结果,暴力枚举根的化这个 sonuson_u 的 dp 结果就会算两次,很不优。

所以可以往 dfs 的状态中加一维 opop 表示是否需要从零开始计算 uu 子树里的答案,即 dfs(u,f,0/1)\text{dfs}(u,f,0/1)

如果 op=0op=0,则按照上面的做法转移即可。如果 op=1op=1,则对于轻儿子全都从零开始计算,uu 不继承这些结果。然后 uu 先继承重儿子的结果,对于轻儿子和 op=0op=0 的做法一样。

可以证明时间复杂度仍然是 O(n1.59X)O(n^{1.59}X)

时间复杂度:O(n1.59X)O(n^{1.59}X)

Code

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
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 205;

int n, x;
int p[kMaxN], b[kMaxN], w[kMaxN], c[kMaxN], res[kMaxN];
int sz[kMaxN], wson[kMaxN];
std::vector<int> G[kMaxN];

inline void chkmax(int &x, int y) { x = (x > y ? x : y); }
inline void chkmin(int &x, int y) { x = (x < y ? x : y); }

void dfs1(int u) {
sz[u] = 1;
for (auto v : G[u]) {
dfs1(v);
sz[u] += sz[v];
if (sz[v] > sz[wson[u]]) wson[u] = v;
}
}

std::array<std::vector<int>, 2> dfs2(int u, std::vector<int> now, bool op) {
if (op) {
for (auto v : G[u]) {
if (v != wson[u]) dfs2(v, now, op);
}
}
std::array<std::vector<int>, 2> f = {now, now};
if (wson[u]) f = dfs2(wson[u], now, op);
for (auto v : G[u]) {
if (v == wson[u]) continue;
f[0] = dfs2(v, f[0], 0)[0];
f[1] = dfs2(v, f[1], 0)[1];
}
if (op) {
for (int i = x; i >= w[u]; --i) {
if (i >= w[u]) chkmax(res[u], f[c[u] ^ 1][i - w[u]] + b[u]);
}
}
for (int i = x; i >= w[u]; --i) chkmax(f[c[u]][i], f[c[u] ^ 1][i - w[u]] + b[u]);
return f;
}

void dickdreamer() {
std::cin >> n >> x;
for (int i = 2; i <= n; ++i) std::cin >> p[i], G[p[i]].emplace_back(i);
for (int i = 1; i <= n; ++i) std::cin >> b[i] >> w[i] >> c[i];
dfs1(1);
std::vector<int> now(x + 1, -1e18);
now[0] = 0;
dfs2(1, now, 1);
for (int i = 1; i <= n; ++i) std::cout << res[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

[ABC311Ex] Many Illumination Plans 题解
https://sobaliuziao.github.io/2025/08/25/post/f2d3db46.html
作者
Egg_laying_master
发布于
2025年8月25日
许可协议