[APIO2012]派遣 dispatching

左偏树。
从下往上求每个子树的战斗力最大值。如果付出的薪水大于m,那么就pop了子树中最贵的那个点,把所有的子树合并起来,用左偏树维护。
(好像所有的master都是1号点....)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=100005;
int ch[N][2],val[N],fa[N],dis[N],lead[N],head[N],ecnt;
int merge(int x,int y) {
	if(!x||!y) return x+y;
	if(val[x]<val[y]) swap(x,y);
	ch[x][1]=merge(ch[x][1],y);
	fa[ch[x][1]]=x;
	if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]);
	dis[x]=dis[ch[x][0]]+1;
	return x;
}
void pop(int &x) {x=merge(ch[x][0],ch[x][1]);return;}
struct Edge{int to,nxt;}e[N];
void add(int bg,int ed){e[++ecnt].nxt=head[bg];e[ecnt].to=ed;head[bg]=ecnt;}
int n,m,master,siz[N],belong[N];
long long ans,sumcost[N];
void dfs(int x) {
	siz[x]=1;sumcost[x]=val[x];belong[x]=x;
	for(int i=head[x];i;i=e[i].nxt) {
		int v=e[i].to;
		dfs(v);
		belong[x]=merge(belong[v],belong[x]);
		siz[x]+=siz[v];
		sumcost[x]+=sumcost[v];
		while(sumcost[x]>m&&siz[x]) sumcost[x]-=val[belong[x]],siz[x]--,pop(belong[x]);
	}
	ans=max(ans,lead[x]*1ll*siz[x]*1ll);
}
main() {
	scanf("%lld%lld",&n,&m);
	int a;
	for(int i=1;i<=n;i++) {
		scanf("%d%d%d",&a,&val[i],&lead[i]);
		if(!a) master=i;
		else {add(a,i);}
	}
	dfs(master);
	cout<<ans;
}
我是咸鱼。转载博客请征得博主同意Orz
原文地址:https://www.cnblogs.com/sdfzhsz/p/9351117.html