Luogu P1552 [APIO2012]派遣 (树形DP, 启发式合并,合并堆)

image

  • 其实就是对每个子树找到最多能派遣的人数乘这个子树根的领导力。 但是n和m的范围不能跑树上背包。
  • 对每个子树维护一个堆,把所有节点的佣金扔到大根堆里面。sum大于m就把堆顶pop掉。堆的size就是最多的人数。
  • 关键是几棵子树怎么合并,直接启发式合并,就是这样。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
typedef __int128_t li;
//#define int long long
#define PII pair<int, int>
const int N = 1e5 + 5;
const int M = 1e2 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double PI =  acos(-1.0);
int n, m;
ll c[N], l[N], id[N];
ll sum[N], ans = 0;
int h[N], e[N << 1], ne[N << 1], idx;
priority_queue<ll, vector<ll>, less<ll> > q[N];
void add( int a, int b ) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void merge(int v, int u) {
    if(q[id[v]].size() > q[id[u]].size()) swap(id[v], id[u]), swap(sum[v], sum[u]);
    while(q[id[v]].size())
    q[id[u]].push(q[id[v]].top()), sum[u] += q[id[v]].top(), q[id[v]].pop();
}
void dfs( int u ) {
    if(c[u] < m) sum[u] += c[u], q[id[u]].push(c[u]);
    for ( int i = h[u]; ~i; i = ne[i] ) {
        int v = e[i];
        dfs(v);
        merge(v, u);
    }
    while (sum[u] > m){
        sum[u] -= q[id[u]].top(), q[id[u]].pop();
    }
    ans = max(ans, l[u] * (ll)q[id[u]].size());
}
int main () {
    memset(h, -1, sizeof h);
    cin >> n >> m;
    for ( int i = 1; i <= n; ++ i ) {
        int bi; cin >> bi >> c[i] >> l[i];
        add(bi, i);
    }
    for ( int i = 1; i <= n; ++ i ) id[i] = i;
    dfs(1);
    cout << ans << endl;
    return 0;
}
posted @ 2022-04-14 22:22  qingyanng  阅读(21)  评论(0编辑  收藏  举报