[贪心] 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;
}

posted @ 2026-01-13 11:20  Zwi  阅读(0)  评论(0)    收藏  举报