397 字
1 分钟
牛客周赛_Round_144_E题
2026-05-19
统计加载中...

题目链接:https://ac.nowcoder.com/acm/contest/134957/E
周日打了一下牛客周赛,感觉这题值得深入学习一下,于是打算复现写一篇博客。
这题我们首先要把题目的意思转换为数学的递推公式,我们需要一步步根据它给的样例推导公式。
我们先来看根节点,根节点需要的总水量就是整个树的水量之和,我们这里用了一个sumsum来记录该节点及其所有子树填满所需要的总水量。所以根节点的水量之和就是sum1sum_1。然后我们看节点22。我们注意到节点2211上的节点高度要比33高,所以如果想要填22瓶的前提是
1.节点33及其子树全部填满了。
2.11瓶的水高度已经达到了w2w_2
我们把这样可以达到22瓶的状态记作base2base_2。(由此也可知base1base_100,因为11瓶一开始就可以灌水)也就是base2=base1+w2+sum3base_2 = base_1 + w_2 + sum_3。而对于base3base_3,我们可以注意到,它是11瓶瓶口高度最低的瓶,所以它不需要加上sumpresum_{pre}。它的公式是base3=base1+w3base_3 = base_1 + w_3,同理我们很容易推导,base4=base3+w4base_4 = base_3 + w_4base5=base3+w5+sum4base_5 = base_3 + w_5 + sum_4base6=base2+w6base_6 = base_2 + w_6base7=base2+w7+sum6base_7 = base_2 + w_7 + sum_6。而知道了能够向该瓶ii注水的水量baseibase_i之后我们再加上当前节点需要的总水量sumisum_i就是我们填满该节点需要的总水量ansians_i了。
具体如何在代码中实现详细我都写在注释里了。
复现代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define endl '\n'
#define mod 998244353
typedef pair<ll, ll> pll;
ll a[200010]; //该该节点的高度
ll fa[200010]; //值对应节点i的父节点u
ll w[200010]; //该节点对应的父节点的瓶口高度
void solved() {
int n;
cin >> n;
for(int i = 1; i <= n;i++) cin >> a[i];
for(int i = 2; i <= n;i++) cin >> fa[i];
for(int i = 2; i <= n;i++) cin >> w[i];
vector<vector<pll>> child(n + 1); //该节点的子节点(对应父节点的瓶身高度,子节点下标)
for(int i = 2; i <= n;i++){
child[fa[i]].emplace_back(w[i], i);
}
vector<ll> cost(n + 1); //该节点及其子树填满的总水量(推导中的sum)
for (int i = n; i >= 1;i--){
cost[i] = a[i];
for(auto& p : child[i]){
cost[i] += cost[p.second];
}
}
vector<ll> sum_v(n + 1); //对应该节点的父节点下,更小瓶身高度的子节点产生的贡献。(推导base公式中sum_i + w_i的那部分)
for(int i = 1; i <= n;i++){
//将该节点的子节点的瓶身高度按从小到大排序
sort(child[i].begin(), child[i].end());
ll cost_pre = 0; //cost的前缀和,用来计算cost的对该节点sum_v的前缀贡献
for (auto& p : child[i]){
ll w_v = p.first;
ll v = p.second;
sum_v[v] = w_v + cost_pre;
cost_pre += cost[v];
}
}
vector<ll> base(n + 1); //达到该节点前我们所需要的总水量
for (int i = 2; i <= n;i++){
base[i] = base[fa[i]] + sum_v[i];
}
//直接输出填满每个节点的所需要的总水量(ans)
for(int i = 1; i <= n;i++){
cout << cost[i] + base[i] << " ";
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int _ = 1;
while(_--){
solved();
}
return 0;
}

我自己推导时的草稿:

替换文本

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

牛客周赛_Round_144_E题
https://mkrari.cn/posts/nowcoder_round_144/
作者
Mkrari
发布于
2026-05-19
许可协议
CC BY-NC-SA 4.0