1. 程式人生 > >【BZOJ2809】[APIO2012] dispatching(左偏樹例題)

【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;
}