• 【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;
    }
    
  • 相关阅读:
    MySQL数据库生成某一年的日历存储过程
    MySQL随笔(四)
    MySQL索引
    MySQL随笔(三)
    MySQL随笔(二)
    MySQL随笔(一)
    设计模式---策略模式
    数组间相互转换 int[]转list
    安装brew -- Homebrew
    mongodb查询方法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2809.html
Copyright © 2020-2023  润新知