bzoj2809 [Apio2012]dispatching——左偏树(可并堆)

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2809

思路有点暴力和贪心,就是 dfs 枚举每个点作为管理者;

当然它的子树中派遣出去的忍者越多越好,只要不超过预算;

所以需要能够合并子树情况的、能反映最大值节点的数据结构,也就是左偏树(可并堆);

所以对于每个点维护一个大根左偏树,当子树内总和超过预算时,就去掉大根堆堆顶,这样最优;

自己的第一棵左偏树!没想到相当简单呢。

左偏树的论文:https://wenku.baidu.com/view/029c886d1eb91a37f1115ce5.html

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int const maxn=1e5+5;
int n,m,head[maxn],ct,mas;
ll ans;
struct N{
    int to,next;
    N(int t=0,int n=0):to(t),next(n) {}
}edge[maxn<<1];
struct T{
    int ls,rs,siz,val,led,fa,dis;
    ll sum;
}t[maxn];
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
void add(int x,int y)
{
    edge[++ct]=N(y,head[x]);head[x]=ct;
    edge[++ct]=N(x,head[y]);head[y]=ct;
}
int merge(int x,int y)
{
    if(!x)return y;
    if(!y)return x;
    if(t[x].val<t[y].val)swap(x,y);
    t[x].rs=merge(t[x].rs,y);
    int ls=t[x].ls,rs=t[x].rs;
    t[x].sum=t[ls].sum+t[rs].sum+t[x].val;
    t[x].siz=t[ls].siz+t[rs].siz+1;
    if(t[ls].dis<t[rs].dis)swap(t[x].ls,t[x].rs);//不是swap(ls,rs) 
    if(t[x].rs)t[x].dis=t[t[x].rs].dis+1;//
    else t[x].dis=0;
    return x;
}
int dfs(int x)
{
    int rt=x;
    for(int i=head[x];i;i=edge[i].next)
    {
        int u=edge[i].to;
        if(u==t[x].fa)continue;
        rt=merge(rt,dfs(u));
    }
//    while(t[x].sum>m)
//    {
//        t[x].sum-=t[x].val;
//        t[x].siz--;
//        x=merge(ls,rs);
//    }
    while(t[rt].sum>m)rt=merge(t[rt].ls,t[rt].rs);
    ans=max(ans,(ll)t[rt].siz*t[x].led);//不是 t[rt].led ,因为是选 x 作为管理者 
    return rt;
}
int main()
{
    n=rd();m=rd();
    for(int i=1;i<=n;i++)
    {
        t[i].fa=rd(); t[i].val=rd(); t[i].led=rd();
        t[i].siz=1; t[i].sum=t[i].val;
        add(t[i].fa,i);
        if(t[i].fa==0)mas=i;
    }
    dfs(mas);
    printf("%lld",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Zinn/p/9185061.html