BZOJ2809 [Apio2012]dispatching 可并堆

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ2809


题意概括

   n个点组成一棵树,每个点都有一个领导力和费用,可以让一个点当领导,然后在这个点的子树中选择一些费用之和不超过m的点,得到领导的领导力乘选择的点的个数(领导可不被选择)的利润。求利润最大值。n≤100000


题解

   做一个类似树形dp的操作。

  维护大根堆,每次从子节点到父节点就是合并所有的子节点的堆。

  利用左偏树。

  然后先删掉大的,直到合法为止。

  好像没什么要讲的。


代码

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
typedef long long LL;
const int N=100005;
struct Gragh{
	int cnt,y[N],nxt[N],fst[N];
	void clear(){
		cnt=0;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
int n,m,L[N],fa[N],C[N],root[N],cnt[N];
struct heap{
	int ls,rs,v,len;
	void set(int a,int b,int c,int d){
		ls=a,rs=b,v=c,len=d;
	}
}h[N];
LL ans=0,tot[N];
LL max(LL a,LL b){
	return a>b?a:b;
}
int merge(int a,int b){
	if (a==0||b==0)
		return a+b;
	if (h[a].v<h[b].v)
		swap(a,b);
	h[a].rs=merge(h[a].rs,b);
	if (h[h[a].ls].len<h[h[a].rs].len)
		swap(h[a].ls,h[a].rs);
	h[a].len=h[h[a].rs].len+1;
	return a;
}
void dfs(int rt){
	tot[rt]=C[rt],cnt[rt]=1;
	root[rt]=rt;
	h[rt].set(0,0,C[rt],0);
	for (int i=g.fst[rt];i;i=g.nxt[i]){
		dfs(g.y[i]);
		root[rt]=merge(root[rt],root[g.y[i]]);
		tot[rt]+=tot[g.y[i]],cnt[rt]+=cnt[g.y[i]];
	}
	while (tot[rt]>m){
		tot[rt]-=h[root[rt]].v;
		root[rt]=merge(h[root[rt]].ls,h[root[rt]].rs);
		cnt[rt]--;
	}
	ans=max(ans,1LL*L[rt]*cnt[rt]);
}
int main(){
	scanf("%d%d",&n,&m);
	g.clear();
	for (int i=1;i<=n;i++){
		scanf("%d%d%d",&fa[i],&C[i],&L[i]);
		g.add(fa[i],i);
	}
	dfs(1);
	printf("%lld",ans);
	return 0;
}

  

原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ2809.html