[Apio2012]dispatching 可合并堆 斜堆

    一道简单的贪心题。很显然是维护一个数据结构,从叶子向根逐级合并,并随时更新答案。 我是用斜堆的,代码量会比splay启发式合并要小一些。 

    斜堆可以参考wiki:click here  

    

    

    每个节点维护一个大根堆,代表这个点子树的薪水。这里可以用斜堆/左偏树/二项堆/Fibonacci堆……   从叶子向根部逐级合并, 每次合并之后,若这个点的子树总薪水大于预算m,就一个一个删除堆顶元素。 直到堆中元素和小于等于m。  

    斜堆每次合并复杂度均摊log(n),总共合并n次。 这部分的复杂度就是nlogn。    每删除一次就要合并一次斜堆的左右儿子,因此每次删除的复杂度也是均摊log(n) ,而每个点最多删除一次,总共最多删除n个点。 这部分的复杂度也是nlogn。

   

#include <iostream>
#include <cstdio>
#include <queue>
#define next nextt
#define MAXN 200000
using namespace std;
typedef long long LL;
struct node
{
    node *l,*r;
    LL val;
    node() {}
}*tree[MAXN],pool[MAXN];
int ind[MAXN],next[MAXN];
LL lead[MAXN],m,ans,sum[MAXN],num[MAXN];
int n;
queue<int> q;
node* merge(node *a,node *b)
{
    if (!a||!b) return a?a:b;
    if (b->val>a->val) swap(a,b);
    a->r=merge(a->r,b);
    swap(a->l,a->r);
    return a;
}
void del(int p)
{
    num[p]--;
    sum[p]-=tree[p]->val;
    tree[p]=merge(tree[p]->l,tree[p]->r);
}
int main()
{
    cin>>n>>m;
    for (int i=1;i<=n;++i)
    {
        scanf("%d%lld%lld",&next[i],&pool[i].val,&lead[i]);
        ++ind[next[i]];
        pool[i].l=pool[i].r=NULL;
        tree[i]=&pool[i];
        sum[i]=pool[i].val;
        num[i]=1;
        if (lead[i]>ans) ans=lead[i];
    }
    for (int i=1;i<=n;++i) if (!ind[i]) q.push(i);
    while (!q.empty())
    {
        int u=q.front();q.pop();
        int v=next[u];
        ind[v]--;
        tree[v]=merge(tree[v],tree[u]);
        sum[v]+=sum[u]; num[v]+=num[u];
        if (!ind[v])
        {
            while (sum[v]>m) del(v);
            if (num[v]*lead[v]>ans) ans=num[v]*lead[v];
            if (next[v]!=0)
                q.push(v);
        }
    }
    cout<<ans<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值