派遣【题解】
第一道左偏树题目,来肝一篇题解。
题目描述
在这个帮派里,有一名忍者被称之为Master。除了Master以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。
现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,你就不需要支付管理者的薪水。
你的目标是在预算内使顾客的满意度最大。这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。
写一个程序,给定每一个忍者i的上级Bi,薪水Ci,领导力Li,以及支付给忍者们的薪水总预算M,输出在预算内满足上述要求时顾客满意度的最大值。
输入输出格式
输入格式:
第一行包含两个整数N和M,其中N表示忍者的个数,M表示薪水的总预算。
接下来N行描述忍者们的上级、薪水以及领导力。其中的第i行包含三个整数Bi,Ci,Li分别表示第i个忍者的上级,薪水以及领导力。Master满足Bi=0,并且每一个忍者的老板的编号一定小于自己的编号Bi<i。
输出格式:
输出一个数,表示在预算内顾客的满意度的最大值。
样例输入:
5 4 0 3 3 1 3 5 2 2 2 1 2 4 2 3 1
样例输出:
6
解:
定义rt数组,记录每个忍者所在的堆;初始时,把每一个忍者都看做一个堆,然后枚举每一位忍者当领导时的薪资,判断是否大于预定薪资,如果大于,弹出对顶,直到符合标准为止。结构体里面除了维护最基本的左偏树信息,还有siz(节点数数量),sum(区间和)。用前向星存边,枚举的时候枚举每一条边即可。(记得把前向星数组开大)
#include<cstdio> #include<iostream> #include<cstring> #include<string> #define ll long long #define lc tr[x].l #define rc tr[x].r using namespace std; const int MAXN=110010; inline void swap(int &x,int &y){x^=y^=x^=y;} inline int read(){ char ch=getchar(); int s=0,w=1;while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}return s*w; } int n,m,head[MAXN],nxt[MAXN],to[MAXN],tot; struct node{ int l,r,fa,siz,w,d; long long sum; }tr[MAXN]; inline void pushup(int x){ tr[x].sum=tr[lc].sum+tr[rc].sum+tr[x].w; tr[x].siz=tr[lc].siz+tr[rc].siz+1; } int merge(int x,int y){ if(!x||!y)return x+y; if(tr[x].w<tr[y].w||(tr[x].w==tr[y].w&&x>y))swap(x,y); rc=merge(rc,y); tr[rc].fa=x; if(tr[lc].d<tr[rc].d)swap(lc,rc); tr[x].d=tr[rc].d+1; pushup(x); return x; } inline void add(int x,int y){ to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } inline void pop(int &x){ tr[lc].fa=tr[rc].fa=0; x=merge(lc,rc); } int rt[MAXN],id,val[MAXN]; ll ans; void solve(int x){ for(int i=head[x];i;i=nxt[i]){ int u=to[i]; solve(u); rt[x]=merge(rt[x],rt[u]); }while(rt[x]&&tr[rt[x]].sum>m)pop(rt[x]); ans=max(ans,val[x]*1ll*tr[rt[x]].siz); } int main(){ n=read(),m=read(); int fa,v; for(int i=1;i<=n;++i){ fa=read(),v=read(),val[i]=read(); if(fa)add(fa,i); tr[i].sum=tr[i].w=v;tr[i].siz=1; rt[i]=merge(rt[i],i); }solve(1); printf("%lld\n",ans); return 0; }
感谢 wjr dalao的指导