[APIO2012]派遣 可并堆(左偏树)

没啥说的,自底向上合并大根堆即可.

一边合并,一边贪心弹堆顶直到堆的总和不大于预算.

Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 100000 + 5
#define ll long long 
using namespace std;
int head[maxn],to[maxn],nex[maxn],cnt,root;
int n;
int siz[maxn],ch[maxn][2],dis[maxn];
ll m,lead[maxn],cost[maxn],ans,sumv[maxn],val[maxn];  
void addedge(int u,int v) { nex[++cnt] = head[u],head[u] = cnt,to[cnt] = v; }
int merge(int a,int b){
    if(!a || !b) return a + b;
    if(val[a] < val[b]) swap(a,b);
    ch[a][1] = merge(ch[a][1],b); 
    if(dis[ch[a][1]] > dis[ch[a][0]]) swap(ch[a][1],ch[a][0]);
    dis[a] = dis[ch[a][0]] + 1; 
    sumv[a] = val[a] + sumv[ch[a][0]] + sumv[ch[a][1]]; 
    siz[a] = siz[ch[a][0]] + siz[ch[a][1]] + 1; 
    return a; 
}
void pop(int &a){
    int t = merge(ch[a][0],ch[a][1]); 
    siz[a] = sumv[a] = val[a] = ch[a][0] = ch[a][1] = 0; 
    a = t; 
}
int dfs(int u){
    int rt = u;
    val[rt] = cost[u];   
    siz[rt] = 1; 
    sumv[rt] = val[rt]; 
    for(int i = head[u]; i ; i = nex[i]){          
        rt = merge(rt,dfs(to[i])); 
        while(sumv[rt] > m) pop(rt); 
    } 
    //printf("%d %d\n",u,siz[rt]); 
    ans = max(ans,siz[rt] * lead[u]); 
    return rt; 
}
int main(){
    //setIO("input");
    scanf("%d%lld",&n,&m); 
    for(int i = 1;i <= n;++i) {
        int a;
        scanf("%d%lld%lld",&a,&cost[i],&lead[i]);
        if(a == 0) root = i;
        else addedge(a,i); 
    }
    dfs(root); 
    printf("%lld",ans); 
    return 0; 
}

  

posted @ 2019-02-10 20:12  EM-LGH  阅读(148)  评论(0编辑  收藏  举报