[APIO 2012]派遣(dispatching)(Leftist-Tree)[bzoj 2809]
本文最后更新于:2017年9月21日 晚上
题目描述
在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿。
在这个帮派里,有一名忍者被称之为Master。除了Master以外,每名忍者都有且仅有一个上级。为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送。
现在你要招募一批忍者,并把它们派遣给顾客。你需要为每个被派遣的忍者支付一定的薪水,同时使得支付的薪水总额不超过你的预算。另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者可以向所有被派遣的忍者发送指令,在发送指令时,任何忍者(不管是否被派遣)都可以作为消息的传递人。管理者自己可以被派遣,也可以不被派遣。当然,如果管理者没有被排遣,你就不需要支付管理者的薪水。
你的目标是在预算内使顾客的满意度最大。这里定义顾客的满意度为派遣的忍者总数乘以管理者的领导力水平,其中每个忍者的领导力水平也是一定的。
写一个程序,给定每一个忍者i的上级Bi,薪水Ci,领导力Li,以及支付给忍者们的薪水总预算M,输出在预算内满足上述要求时顾客满意度的最大值。
输入格式
从标准输入读入数据。 第一行包含两个整数N和M,其中N表示忍者的个数,M表示薪水的总预算。 接下来N行描述忍者们的上级、薪水以及领导力。其中的第i行包含三个整数Bi , Ci , Li 分别表示第i个忍者的上级,薪水以及领导力。Master满足Bi = 0,并且每一个忍者的老板的编号一定小于自己的编号 Bi < i。
输出格式
输出到标准输出。
输出一个数,表示在预算内顾客的满意度的最大值。
样例
输入样例#1:
5 4
0 3 3
1 3 5
2 2 2
1 2 4
2 3 1
输出样例#1:
6
数据范围与提示
样例说明 :
如果我们选择编号为1的忍者作为管理者并且派遣第三个和第四个忍者,薪水总和为4,没有超过总预算4。因为派遣了2个忍者并且管理者的领导力为3,用户的满意度为2 × 3 = 6,是可以得到的用户满意度的最大值。
数据范围:
$1≤N≤100,000$ 忍者的个数;
$1≤M≤1,000,000,000$薪水总预算;
$0≤Bi<i$ 忍者的上级的编号;
$1≤Ci≤M $忍者的薪水;
$1≤Li≤1,000,000,000$ 忍者的领导力水平。
解题思路:
由于并且每一个忍者的老板的编号一定小于自己的编号 Bi < i
,那么我们可以由n到1,(表从属的树从下至上),维护多颗左偏树(大根),并且依次合并,一旦某点所在左偏树sum大于m,一直pop直到其左偏树sum<=m,每次算出的num*L[i]与ans取max,最后ans即为答案。
细节
1.long long必开。
2.合并时,将该点所在左偏树root节点与儿子节点所在左偏树root节点合并,并非它们本身。
3.区分原树与左偏树。
Code:
//spaceskynet 2017-09-01 Leftist-Tree (Biggest-First)
#include<iostream>
#include<cstdio>
#include<vector>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
struct node
{
int ls,rs,val,dis;
node(){}
node(int ls,int rs,int val,int dis):
ls(ls),rs(rs),val(val),dis(dis){}
}Ltree[maxn];
vector<int> s[maxn];
ll ans=0;
int n,m,B[maxn],C[maxn],L[maxn],root[maxn],sum[maxn],num[maxn];
int pop(int);
int merge(int,int);
int Find(int);
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&B[i],&C[i],&L[i]);
s[B[i]].push_back(i);
root[i]=i;
}
for(int i=n;i>0;i--)
{
if(C[i]<=m)
{
Ltree[i]=node(0,0,C[i],0);
sum[i]=C[i],num[i]=1;
}
else Ltree[i]=node(0,0,0,0);
for(int j=(int)s[i].size()-1;~j;j--)
{
root[i]=merge(root[i],root[s[i][j]]);
sum[i]+=sum[s[i][j]];
num[i]+=num[s[i][j]];
while(sum[i]>m) root[i]=pop(i);
}
ans=max(ans,1ll*L[i]*num[i]);
}
printf("%lld\n",ans);
return 0;
}
int pop(int x)
{
int ls=Ltree[root[x]].ls,rs=Ltree[root[x]].rs;
sum[x]-=Ltree[root[x]].val;
--num[x];
Ltree[root[x]]=node(0,0,C[root[x]],0);
// sum[root[x]]=C[root[x]],num[root[x]]=1;
return merge(ls,rs);
}
int merge(int l,int r)
{
if(l==0) return r;
if(r==0) return l;
if(Ltree[l].val<Ltree[r].val) swap(l,r);
Ltree[l].rs=merge(Ltree[l].rs,r);
// Ltree[Ltree[l].rs].fa=l;不用维护fa
if(Ltree[Ltree[l].ls].dis<Ltree[Ltree[l].rs].dis) swap(Ltree[l].ls,Ltree[l].rs);
if(Ltree[l].rs==0) Ltree[l].dis=0;
else Ltree[l].dis=Ltree[Ltree[l].rs].dis+1;
return l;
}