[贪心] P5521 梅深不见冬
posted on 2024-03-14 10:40:33 | under | source
小清新题,不看题解做出来了(喜。
考虑树上递推,\(f_i\) 表示从 \(i\) 出发,使得 \(i\) 上放了 \(w_i\) 朵梅花的最少初始携带数。
若 \(u\) 有 \(m\) 个孩子分别为 \(v_1...v_m\),则转移为:\(f_u=\max(f_{v_i} + \sum\limits_{j=1}^{i-1}w_{v_j},w_u+\sum\limits_{i=1}^{m}w_{v_i})\)。
但是我们可以随意选择遍历儿子的顺序,即 \(v_1...v_m\) 是可以任意调整的。
很容易想到按 \(w\) 或按 \(f\) 排,不幸的是这两种方式都是错误的。
不过我们仍然猜测可通过一次排序,求出使得 \(f_u\) 最小的 \(v\) 数组顺序。
为了设计正确的比较函数,我们需对相邻两项 \(v_i,v_{i+1}\) 进行考察。
(就是邻项交换法啦,若能说明在某种条件下,调换相邻元素顺序不会使结果更劣,则可以通过不断调换相邻元素得到最优解。)
显然调换它们顺序不会影响 \([1,i-1]\) 和 \([i+2,m]\) 位的取值。设 \(S=\sum\limits_{j=1}^{i-1} w_{v_j}\),调换前:\(\max(S+f_{v_i},S+w_{v_i}+f_{v_{i+1}})\),调换后:\(\max(S+f_{v_{i+1}},S+w_{v_{i+1}}+f_{v_i})\)。
去掉相同项,由邻项交换可知,\(\forall i,\max(f_{v_i},w_{v_i}+f_{v_{i+1}})\le\max(f_{v_{i+1}},w_{v_{i+1}}+f_{v_i})\) 时当前局面为最优解。
于是就这样设计比较函数就好啦~
后话:事实上,需证明比较函数满足严格弱序才能使用快排。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5;
int n, p, w[N], f[N];
vector<int> to[N];
inline bool cmp(int a, int b) {return max(f[a], w[a] + f[b]) < max(f[b], w[b] + f[a]);}
inline void dfs(int u){
for(auto v : to[u]) dfs(v);
sort(to[u].begin(), to[u].end(), cmp);
int res = 0;
for(auto v : to[u]) f[u] = max(f[u], f[v] + res), res += w[v];
f[u] = max(f[u], res + w[u]);
}
signed main(){
cin >> n;
for(int i = 2; i <= n; ++i) scanf("%lld", &p), to[p].push_back(i);
for(int i = 1; i <= n; ++i) scanf("%lld", &w[i]);
dfs(1);
for(int i = 1; i <= n; ++i) printf("%lld ", f[i]);
return 0;
}

浙公网安备 33010602011771号