洛谷P5521 [yLOI2019] 梅深不见冬 题解

P5521 [yLOI2019] 梅深不见冬

题目链接

(这题的暴力太难写了QAQ)

思路:看完题目很容易发现,在某点放一朵梅花本质上就是用 DFS 的方式遍历该点的子树。

那么我们只要找到在该点的子节点上放梅花的最小花费就可以了。

本题的特殊样例已经在一定程度上提醒了我们正解的思路。

首先观察特殊样例:节点 i 最多只有两个儿子 u,v 时,可以分开枚举先走 u 和先走 v 的两种情况。

设 $ans_i$ 表示节点 i 的答案,那么不难得到:

若节点 i 为叶子结点,则 $ans_i = w_i$。

否则 $ans_i = \min(ans_u + \max(ans_v - ans_u + w_u , 0) , ans_v + \max(ans_u - ans_v + w_v , 0))$

感性地理解一下:先走 u 时,首先要准备 $ans_u$ 朵梅花,在 u 结点放完梅花后,取走它子节点上的 $ans_u - w_u$ 朵梅花。

然后走 v 时,若满足 $ans_u - w_u \le ans_v$,则答案要加上 $ans_v - ans_u + w_u$。

否则若 $ans_u - w_u \gt ans_v$,则答案为 $ans_u$。

先走 v 时同理。

从刚刚的思考里不难发现:u,v 两个节点中,走 $ans_k - w_k (k \in \{u,v\})$ 较大的节点一定更优。

考虑拓展到一般情况,若点 i 有 m 个子节点 $son_1,son_2 \cdots son_m$ ,如何排列。

根据刚刚的思考和生活经验,考虑贪心:按 $ans_k - w_k (k \in \{son_1,son_2 \cdots son_m \})$ 降序排序,再按上述方法统计。

 


 

证明(参考官方题解):

微扰。对于子节点 u,v ,设 $a_u = ans_u - w_u \gt a_v = ans_v - w_v$。

先走 u 时,答案为 $S = max(ans_u , ans_v + w_u)$。

先走 v 时,答案为 $T = max(ans_v , ans_u + w_u)$。

$\because a_u = ans_u - w_u,a_v = ans_v - w_v$

$\therefore S = max(a_u + w_u , ans_v + w_u) = w_u + max(a_u , ans_v),$

$T = max(a_v + w_v , ans_u + w_v) = w_v + max(a_v , ans_u) = w_v + w_u + a_u$。

若 $a_u \gt ans_v$,则 $S = w_u + a_u \lt w_u + a_u + w_v = T$。

若 $a_u \le ans_v$,则 $S = w_u + a_v + w_v \lt w_u + a_u + w_v = T$。

故 $S$ 恒小于 $T$,由数学归纳可得,按 $ans - w$ 降序排序必定最优。

证毕。

 


 

那么我们只需要写一个 DFS,在叶子结点直接统计答案,其余结点按上述方法统计即可。

代码如下:

洛谷P5521 [yLOI2019] 梅深不见冬 题解
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <vector>
 5 using namespace std;
 6 const int maxn = 1e5 + 10;
 7 int n,w[maxn];
 8 struct node {
 9     int id,val,ans;
10     node() {
11         id = val = ans = 0;
12     }
13     node(int id,int val,int ans):id(id),val(val),ans(ans){}
14     bool operator < (const node& p)const {
15         return ans - val > p.ans - p.val;
16     }
17 }a[maxn];
18 bool cmp(int p,int q) {
19     return a[p] < a[q];
20 }
21 vector<int> son[maxn];
22 namespace work {
23     int ans[maxn],b[maxn],f[maxn];
24     void dfs(int u) {
25         if(son[u].empty()) {
26             a[u].ans = w[u];
27             return ;
28         }
29         for(int i = 0;i < son[u].size();++ i) {
30             int v = son[u][i];
31             dfs(v);
32         }
33         sort(son[u].begin() , son[u].end() , cmp);
34         int cnt = 0,ret = 0;
35         for(int i = 0;i < son[u].size();++ i) {
36             int v = son[u][i];
37             if(a[v].ans <= ret) {
38                 ret -= a[v].ans;
39             }
40             else {
41                 cnt += a[v].ans - ret;
42                 ret = 0;
43             }
44             ret += a[v].ans - a[v].val;
45         }
46         if(w[u] <= ret)a[u].ans = cnt;
47         else a[u].ans = cnt + w[u] - ret;
48         return ;
49     }
50     int main() {
51         scanf("%d",&n);
52         for(int i = 2;i <= n;++ i) {
53             int x;
54             scanf("%d",&x);
55             son[x].push_back(i);
56         }
57         for(int i = 1;i <= n;++ i)scanf("%d",&w[i]),a[i] = node(i , w[i] , 0x3f3f3f3f);
58         dfs(1);
59         for(int i = 1;i <= n;++ i) {
60             printf("%d ",a[i].ans);
61         }
62         return 0;
63     }
64 }
65 int main() {
66     work::main();
67     return 0;
68 }
QAQ

 

上一篇:E1. Distance Tree (easy version) 题解(思维)


下一篇:P1576 最小花费(最长路径)