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

文章作者: SpaceSkyNet
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 SpaceSkyNet !
  目录
评论