咕咕树

4596B 咕咕树

Summarize

题目概括咕咕

Solution

高压下的代码实现能力堪忧。比赛 1 小时没敲出来,订正 10 分钟满分。

官方题解给的是树形背包,但并不是特别好实现。

定义状态 $f[u][i]$ 表示以 $u$ 为根的子树,有一条以 $u$ 为起点且长度恰好为 $i$ 的链,最少花费的代价。

考虑状态转移: $f[u][0]$ 显然是切断当前节点后,再将每个子树切断;$f[u][1]$ 即为切出一条长度为 $i$ 的链,对其余子树的链长度进行讨论。
$$
f[u][0]=a[u]+\sum_{v\in son(u)}\min_{j=0}^{l-1}\lbrace f[v][j]\rbrace\
f[u][1]=f[s][i-1]+\sum_{v\in son(u),v\not= s}\min_{j=0}^{\min(i-1,l-i-1)}\lbrace f[v][j]\rbrace
$$
考虑优化。对所有 $f[u][0..l-1]$ 做前缀最小值,并记 $g[u][i]=\sum_{v\in son(u)}f[v][j]$,则转移方程简化为:
$$
f[u][0]=a[u]+g[u][l-1]\
f[u][1]=f[s][i-1]-f[s][\min(i-1,l-i-1)]+g[u][\min(i-1,l-i-1)]
$$
注意叶节点的 $f[u][1]$ 应赋值为 $0$。

时间复杂度 $O(n^2)$。

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

const int MAXN = 5005;

int n, l;
int a[MAXN];
vector<int> G[MAXN];
int f[MAXN][MAXN];
int g[MAXN][MAXN];

void dfs(int u, int fa) {
for (vector<int>::iterator it = G[u].begin(); it != G[u].end(); it++) {
int v = *it;
if (v == fa) continue;
dfs(v, u);
for (int i = 0; i < l; ++i)
g[u][i] += f[v][i];
}
for (vector<int>::iterator it = G[u].begin(); it != G[u].end(); it++) {
int v = *it;
if (v == fa) continue;
for (int i = 1; i < l; ++i) {
f[u][i] = min(f[u][i], g[u][min(i - 1, l - i - 1)] - f[v][min(i - 1, l - i - 1)] + f[v][i - 1]);
}
}
f[u][0] = a[u] + g[u][l - 1];
if (u != 1 && G[u].size() == 1) f[u][1] = 0;
for (int i = 1; i < l; ++i)
f[u][i] = min(f[u][i], f[u][i - 1]);
}

int main() {
int sum = 0;
memset(f, 0x3f, sizeof(f));
scanf("%d%d", &n, &l);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]), sum += a[i];
if (l == 0) return printf("%d", sum), 0;
for (int i = 1; i < n; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v), G[v].push_back(u);
}
dfs(1, 0);
printf("%d", f[1][l - 1]);
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×