bzoj2809: [Apio2012]dispatching

题目链接

bzoj2809: [Apio2012]dispatching

题解

领导关系形成一棵树,那么答案为\(ans=max{L[u]\times k}\),其中k代表以u为根的子树中选出的节点数个数(设这些节点为\(v_1,v_2\cdots v_k\)且有\(\sum_{i=1}^{k}C[v_i]\leq M\)

每个节点建一个大根堆,维护薪水值。初始时若自己满足条件就选自己,否则不选。从叶子节点往上推,对于每个节点,将它与它的儿子节点合并。如果当前的选择费用超出了M,就pop出堆里的最大的,一直到不超过M为止。咋维护呢,好像有个东西叫左偏树来着QAQQQQ
动态维护当前选择的节点数num和选择费用sum即可。

代码

#include<cstdio>
#include<algorithm> 
using std::swap;
inline int read() {
    int x = 0,f = 1;
    char c = getchar();
    while(c < '0' || c > '9') {if(c == '-')f = -1;c = getchar(); }
    while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar(); 
    return x * f;
} 
#define int long long
int n,m; 
const int maxn = 200007; 
int c[maxn],b[maxn],l[maxn];  
int num = 0,head[maxn]; 
struct node { 
    int v,next; 
}edge[maxn << 1]; 
void add_edge(int u ,int v) {
    edge[++num].v = v,edge[num].next = head[u],head[u] = num; 
} 
struct Leftist_Tree {
    int lc,rc,dis,val; 
    bool operator < (const Leftist_Tree &q) const {
        return val > q.val; 
    }  
} t[maxn];  
int fa[maxn]; 
#define lc t[x].lc 
#define rc t[x].rc 
int merge(int x,int y) { 
    if(!x || !y) return x + y;  
    if(t[y] < t[x]) swap(x,y);  
    if(t[y].val == t[x].val && x > y) swap(x,y);  
    fa[rc = merge(rc,y)] = x;  
    if(t[lc].dis < t[rc].dis) swap(lc,rc);  
    t[x].dis = t[rc].dis + 1;  
    return x ;  
} 
int pop(int x) { 
    fa[lc] = fa[rc] = 0; 
    int ret = merge(lc,rc); 
    lc = rc = 0; 
    return ret; 
} 
#undef lc 
#undef rc  
int sum[maxn],size[maxn],root[maxn],ans;  

void dfs(int u) { 
    root[u] = ++num; 
    t[num].val = c[u]; 
    size[u] = 1;sum[u] = c[u]; 
    for(int i = head[u]; i;i = edge[i].next) { 
        int v = edge[i].v; 
        dfs(v); 
        sum[u] += sum[v]; 
        size[u] += size[v]; 
        root[u] = merge(root[u],root[v]); 
    }  
    while(sum[u] > m)  { 
        sum[u] -= t[root[u]].val; 
        root[u] = pop(root[u]); 
        size[u] --; 
    } 
    ans = std::max(ans,l[u] * size[u]); 
    return ; 
} 
main() { 
    n = read(),m = read() ; 
    t[0].dis = -1;
    int rt = 0; 
    for(int i = 1;i <= n;++ i) { 
        b[i] = read(), c[i] = read(),l[i] = read(); 
        if(b[i]) add_edge(b[i],i); 
        else rt = i; 
    } 
    num  = 0; 
    dfs(rt); 
    printf("%lld\n",ans); 
    return 0;
}

转载于:https://www.cnblogs.com/sssy/p/8996308.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值