【bzoj2809】[Apio2012]dispatching (左偏树)

我们需要枚举根,然后从其子树内选尽量多的点,薪水不超过M,可是暴力复杂度不对。
于是考虑自下而上合并树(开始每棵树内只有一个节点,就是自己)

每个树是一个堆,我们维护树的节点个数和薪水总和,合并时,不断弹出堆顶薪水最大的直到薪水总和不超过M,然后用领导力*节点个数更新答案。
发现这个模型就是裸的左偏树。

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
 
typedef long long LL;
 
#define N 100010
 
int l[N],r[N],fa[N];
int tree[N],siz[N],d[N];
 
LL ld[N],c[N],sum[N];
 
int n;
 
LL m,ans;
 
int merge(int x,int y)
{
    if (!x)
        return y;
    if (!y)
        return x;
    if (c[x]<c[y])
        swap(x,y);
    r[x]=merge(r[x],y);
    if (d[r[x]]>d[l[x]])
        swap(l[x],r[x]);
    d[x]=d[r[x]]+1;
    sum[x]=sum[l[x]]+sum[r[x]]+c[x];
    siz[x]=siz[l[x]]+siz[r[x]]+1;
    return x;
}
 
int main()
{
    scanf("%d%lld",&n,&m);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%lld%lld",&fa[i],&c[i],&ld[i]);
        tree[i]=i;
        siz[i]=1;
        sum[i]=c[i];
    }
    for (int i=n;i>=1;i--)
    {
        while (sum[tree[i]]>m)
            tree[i]=merge(l[tree[i]],r[tree[i]]);
        ans=max(ans,siz[tree[i]]*ld[i]);
        if (i!=1)
            tree[fa[i]]=merge(tree[fa[i]],tree[i]);
    }
    printf("%lld",ans);
    return 0;
}

  

原文地址:https://www.cnblogs.com/yangjiyuan/p/5761022.html