[APIO2012]派遣 [可并堆]

传送门

首先想到枚举每个点作为领导 , 然后我们需要快速查出x为根的子树可以有多少个节点

满足这些节点加起来不超过m

于是我们从下到上合并 , 维护一个小根堆(大的在上) , 如果堆的和>m就弹出堆顶


#include<bits/stdc++.h>
#define N 100050
#define LL long long
using namespace std;
int siz[N],sum[N],l[N],r[N],val[N];
int n,m,b[N],c[N],w[N],rt[N]; LL ans;
int first[N],next[N],to[N],tot;
void add(int x,int y){
	next[++tot]=first[x],first[x]=tot,to[tot]=y;
}
int Merge(int x,int y){
	if(!x||!y) return x+y;
	if(val[x]<val[y]) swap(x,y);
	l[x]=Merge(l[x],y);
	if(rand()%2) swap(l[x],r[x]);
	return x;
}
void Pop(int x){
	int now = rt[x];
	rt[x] = Merge(l[now],r[now]);
	siz[x]-- , sum[x]-=val[now];
	l[now]=r[now]=val[now]=0;
}
void dfs(int u){
	for(int i=first[u];i;i=next[i]){
		int t=to[i]; dfs(t);
		siz[u] += siz[t] , sum[u] += sum[t];
		rt[u] = Merge(rt[u],rt[t]);
		while(sum[u] > m) Pop(u);
	}
	siz[u]++; sum[u] += c[u]; val[u]=c[u];
	rt[u] = Merge(u,rt[u]);
	while(sum[u] > m) Pop(u);
	ans = max(ans,(LL)w[u]*siz[u]);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&b[i],&c[i],&w[i]);
		add(b[i],i);
	}
	dfs(1); printf("%lld",ans); return 0;
}

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/84673721