1. 程式人生 > >BZOJ2809 [Apio2012]dispatching 可並堆

BZOJ2809 [Apio2012]dispatching 可並堆

style 操作 isp eap 超過 add 傳送門 int 什麽

歡迎訪問~原文出處——博客園-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;
}

  

BZOJ2809 [Apio2012]dispatching 可並堆