P1552 [APIO2012]派遣

https://www.luogu.com.cn/problem/P1552
线段树合并

题意就是,在一棵树中,每个点有一个花费,还有一个权值,需要找出一堆点,使得它们的花费总和小于等于 (m),且全都在同一子树中,最大化点的个数乘以这个子树的根的权值
子树的根可以不选

枚举子树的根,肯定是选子树中前 (k) 小花费的点,可以用类似与前缀和的二分来做
但是如果只是前缀和,那么没做一次就都得 (O(n)),因为无法通过儿子的前缀和加上其他某些信息来推知父亲的前缀和
所以要用到线段树,以花费为下标键动态开点线段树,从线段树上二分,并从儿子往父亲合并

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN puts("")
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 100006
int n,m;
struct Node{
	Node *ls,*rs;
	long long sum;
	int cnt;
}dizhi[N*80],*root[N],*null=&dizhi[0];
int tot;
int fa[N],L[N],C[N];
inline void New(Node *&a){
	a=&dizhi[++tot];a->ls=a->rs=null;
}
void change(Node *tree,int l,int r,int pos){
	tree->sum+=pos;tree->cnt++;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(pos<=mid){
		if(tree->ls==null) New(tree->ls);
		change(tree->ls,l,mid,pos);
	}
	else{
		if(tree->rs==null) New(tree->rs);
		change(tree->rs,mid+1,r,pos);
	}
}
Node *merge(Node *a,Node *b,int l,int r){
	if(a==null) return b;
	if(b==null) return a;
	if(l==r){
		a->sum+=b->sum;a->cnt+=b->cnt;
		return a;
	}
	int mid=(l+r)>>1;
	a->ls=merge(a->ls,b->ls,l,mid);
	a->rs=merge(a->rs,b->rs,mid+1,r);
	a->sum=a->ls->sum+a->rs->sum;
	a->cnt=a->ls->cnt+a->rs->cnt;
	return a;
}
int ask(Node *tree,int l,int r,int lim){
	if(tree==null) return 0;
	if(l==r) return lim>=tree->sum;
	int mid=(l+r)>>1;
	if(lim>=tree->ls->sum) return tree->ls->cnt+ask(tree->rs,mid+1,r,lim-tree->ls->sum);
	return ask(tree->ls,l,mid,lim);
}
int main(){
	n=read();m=read();
	for(reg int i=1;i<=n;i++){
		fa[i]=read();C[i]=read();L[i]=read();
		New(root[i]);
		change(root[i],1,1e9,C[i]);
	}
	long long ans=0;
	for(reg int i=n;i;i--){//题目中明确了一个点的父亲编号比他小
		ans=std::max(ans,(long long)ask(root[i],1,1e9,m)*L[i]);
		if(fa[i]) merge(root[fa[i]],root[i],1,1e9);
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/13841969.html