【BZOJ2809】[APIO2012] dispatching(左偏樹例題)
阿新 • • 發佈:2018-11-01
大致題意: 有\(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; }