洛谷P5521 [yLOI2019] 梅深不见冬 题解
题意:
给定一棵树,根节点为 $1$ ,有点权 $w_i$ ,cxy 想在在结点上放满梅花。
她从这棵树的 $1$ 号节点出发,沿着树上的边行走。
设当前在 $u$ ,则 cxy 每次只能走到一个没有走过的儿子结点 $v$ ,或者回到 $u$ 的父亲。
当且仅当一个结点 $u$ 的所有儿子 $v$ 都放满了 $w_v$ 朵,才可以在 $u$ 上放置梅花。
梅花可以随时收回,求每个结点放上梅花的最小花费。
数据范围:
$1 \leq n \leq 10^5 + 4,~1 \leq w_i \leq 1000$。
设第 $i$ 个结点放梅花的最小花费为 $f_i$ ,不难发现
后者是最小花费的下界
观察第一部分
这部分显然与 $\text{son}(u)$ 的排列有关
如何求出这个最优排列?
贪心,按 $g_u = f_u - w_u$ 降序排序即可
考虑 $u$ 仅有 $2$ 个子结点 $x,y$ 的情况,
设 $x$ 在 $y$ 之前更优,则
若 $f_y + w_x \ge f_x$ ,
因为 $f_y + w_x > f_y$ ,所以右侧取 $f_x+w_y$ 才可能成立
则可得
移项得
若 $f_y + w_x < f_x$
则 $f_x > f_y$,故右侧取 $f_x+w_y$ 时才可能成立
显然有 $f_x < f_x + w_y$
综上所述,按 $g_u = f_u - w_u$ 降序排序可以获得最优解
时间复杂度 $\mathcal{O}(n \log n)$
代码:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e5+15)
vector<int> son[N];
int n,m,w[N],f[N];
int cmp(int x,int y)
{return f[y]-w[y]<f[x]-w[x];}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n;
for(int i=2,u; i<=n; i++)
cin >> u,son[u].push_back(i);
for(int i=1; i<=n; i++) cin >> w[i];
for(int i=n; i>=1; i--)
{
sort(son[i].begin(),son[i].end(),cmp);
int sum=0;
for(int v : son[i])
f[i]=max(f[i],f[v]+sum),sum+=w[v];
f[i]=max(f[i],sum+w[i]);
}
for(int i=1; i<=n; i++)
cout << f[i] << " \n"[i==n];
return 0;
}