D.Dance
原题链接
https://ac.nowcoder.com/acm/contest/123/D
思路
题意比较复杂,大致意思是说给了一棵树,根节点是0号点,然后需要我们从叶节点开始往上走,每个节点都有一个价值和一个次数,我们每次往上走需要花费一个次数,问所有从叶节点开始往上走到根节点的路径价值之和是多少。
图片有助于理解:
那么这里可以先dfs出来每一个叶节点的总价值,然后将叶节点从大到小排序,先从能提供大价值的选,每次将这条路径上的次数减掉当前最小次数即可。
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 100010;
typedef pair<int, int> PII;
vector<int> son[N];
int cnt[N], p[N], fa[N];
PII leaf[N];
int num;
bool cmp(PII a, PII b) {
if (a.second != b.second)
return a.second > b.second;
}
void dfs(int now, int sum) {
if (!son[now].size()) {
leaf[num].first = now;
leaf[num ++ ].second = sum;
return;
}
for (auto x: son[now]) dfs(x, sum + p[x]);
}
int get_min(int x, int c) {
if (x == 0) return c;
if (cnt[x] == 0) return 0; // 剪枝,不加会超时,因为这棵树可能非常大
if (cnt[x] < c) {
c = cnt[x];
cnt[x] = 0; // 这个节点的次数直接清0,因为可以全部选上
}
else cnt[x] -= c;
return get_min(fa[x], c);
}
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
son[a].push_back(i);
fa[i] = a, cnt[i] = b, p[i] = c;
}
dfs(0, 0);
sort(leaf, leaf + num, cmp);
long long ans = 0;
for (int i = 0; i < num; i ++ ) {
int x = leaf[i].first;
int min_ = get_min(x, cnt[x]); // 记录这条路径上最小的次数
ans += (long long)leaf[i].second * min_;
}
printf("%lld\n", ans);
return 0;
}