【BZOJ2809】[APIO2012] dispatching(左偏树例题)

点此看题面

大致题意:(N)名忍者,每名忍者有三个属性:上司(B_i),薪水(C_i)和领导力(L_i)。你要选择一个忍者作为管理者,然后在所有被他管理的忍者中选择若干名忍者,使薪水总和不超过预算(M)。现让你最大化被派遣的忍者总数乘以管理者的领导力水平。

关于左偏树

这道题是一道比较裸的左偏树板子题。

左偏树,主要用途是实现堆的合并,在这一类的题目中还是比较实用的。

大致思路

如果你会左偏树,那么这题就是一道水题。

首先考虑遍历题目中给出的树,然后对每一个节点开一个大根堆,每次把超过预算的多余部分弹出,更新(ans)之后再与父亲的堆进行合并。

一个细节就是当前节点可能会被弹出,所以我们要用(Top_x)来记录当前节点堆的堆顶,然后对(Top_x)进行操作。

代码

#include<bits/stdc++.h>
#define N 100000
#define swap(x,y) (x^=y^=x^=y)
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define LL long long
using namespace std;
int n,m,ee=0,lnk[N+5],Top[N+5],cnt[N+5],Cost[N+5],Val[N+5];LL ans=0,tot[N+5];
struct edge
{
    int to,nxt;
}e[N+5];
class FIO
{
    private:
        #define Fsize 100000
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
        #define pc(ch) (void)(putchar(ch))
        int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize];
    public:
        inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));}
        inline void write(LL x) {if(!x) return pc('0');while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
}F;
class Class_LeftistTree//左偏树模板
{
    private:
        struct Tree
        {
            int Val,Dis,Exist,Father,Son[2];
            Tree(int x=0):Val(x){Dis=Father=Son[0]=Son[1]=0,Exist=1;}
        }node[N+5];
        inline int Merge(int x,int y)
        {
            if(!x||!y) return x+y;
            if(node[x].Val<node[y].Val) swap(x,y);
            if(node[node[x].Son[1]=Merge(node[x].Son[1],y)].Father=x,node[node[x].Son[0]].Dis<node[node[x].Son[1]].Dis) swap(node[x].Son[0],node[x].Son[1]);
            return node[x].Dis=node[node[x].Son[1]].Dis+1,x;
        }
        inline int TopPos(int x) {while(node[x].Father) x=node[x].Father;return x;}
    public:
        Class_LeftistTree() {node[0].Dis=-1,node[0].Exist=0;}
        inline void Init(int n,int *data) {for(register int i=1;i<=n;++i) node[i]=Tree(data[i]);}
        inline void Union(int x,int y) {if((x=TopPos(x))^(y=TopPos(y))&&node[x].Exist&&node[y].Exist) Merge(x,y);}
        inline int PopTop(int x) {return node[x=TopPos(x)].Val=node[x].Dis=-1,node[x].Exist=0,node[node[x].Son[0]].Father=node[node[x].Son[1]].Father=0,Merge(node[x].Son[0],node[x].Son[1]);}
        inline int TopVal(int x) {return node[TopPos(x)].Val;}
}LeftistTree;
inline void dfs(int x)//遍历
{
    register int i;
    for(i=lnk[Top[x]=x],tot[x]=Cost[x],cnt[x]=1;i;i=e[i].nxt) dfs(e[i].to),LeftistTree.Union(x,Top[e[i].to]),tot[x]+=tot[e[i].to],cnt[x]+=cnt[e[i].to];//先遍历子节点,然后从子节点更新信息
    while(tot[x]>m) tot[x]-=LeftistTree.TopVal(Top[x]),--cnt[x],Top[x]=LeftistTree.PopTop(Top[x]);//弹出多余的元素
    Gmax(ans,1LL*Val[x]*cnt[x]);//更新ans
}
int main()
{
    register int i,x,rt;
    for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(x),F.read(Cost[i]),F.read(Val[i]),(x?add(x,i):rt=i);
    return LeftistTree.Init(n,Cost),dfs(rt),F.write(ans),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2809.html