D.Dance

原题链接
https://ac.nowcoder.com/acm/contest/123/D

思路
题意比较复杂,大致意思是说给了一棵树,根节点是0号点,然后需要我们从叶节点开始往上走,每个节点都有一个价值和一个次数,我们每次往上走需要花费一个次数,问所有从叶节点开始往上走到根节点的路径价值之和是多少。
图片有助于理解:
image

那么这里可以先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;
}
posted on 2021-05-07 15:43  Laurance  阅读(44)  评论(0编辑  收藏  举报