P1552-[APIO2012]派遣【左偏树】

正题

题目链接:https://www.luogu.com.cn/problem/P1552


题目大意

一个 n n 个点森林,每个点有价值和代价,选择一个点并在这个点的子树中选择一些点使得。

选择的点数*该点的价值 最大且选择的点的代价之和不超过 m m


解题思路

我们对于每个点,我们将所有子节点处理完后将子节点的左偏树合并,然后不停弹出代价最大的点知道代价之和小于等于 m m 即可。

时间复杂度 O ( n log n ) O(n\log n)


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=110000;
ll n,m,val[N],lead[N],sum[N],ans;
struct Left_Tree{
	ll t[N][2],fa[N],dis[N];
	#define ls t[x][0]
	#define rs t[x][1]
	ll Get(ll x)
	{return (fa[x]==x)?(x):(fa[x]=Get(fa[x]));}
	ll Merge(ll x,ll y){
		if(!x||!y) return x+y;
		if(val[x]<val[y]||(val[x]==val[y]&&x<y))
			swap(x,y);
		rs=Merge(rs,y);
		if(dis[ls]<dis[rs])swap(ls,rs);
		fa[rs]=fa[ls]=x;dis[x]=dis[rs]+1;
		sum[x]=sum[rs]+sum[ls]+val[x];
		return  x;
	}
	void Del(ll x){
		val[x]=0;
		fa[ls]=ls;fa[rs]=rs;
		fa[x]=Merge(ls,rs);
	}
	#undef ls
	#undef rs
}T;
struct edge_node{
	ll to,next;
}a[N];
ll ls[N],tot;
bool k[N];
void addl(ll x,ll y)
{
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;
}
ll dfs(ll x){
	ll siz=1;
	for(ll i=ls[x];i;i=a[i].next){
		ll y=a[i].to;siz+=dfs(y);
		T.Merge(T.Get(x),T.Get(y));
	}
	while(sum[T.Get(x)]>m)
		T.Del(T.Get(x)),siz--;
	ans=max(ans,siz*lead[x]);
	return siz;
}
int main()
{
	scanf("%lld%lld",&n,&m);
	for(ll i=1;i<=n;i++){
		ll x;
		scanf("%lld%lld%lld",&x,&val[i],&lead[i]);
		sum[i]=val[i];T.fa[i]=i;
		if(x)addl(x,i);else k[i]=1;
	}
	for(ll i=1;i<=n;i++)
		if(k[i]) dfs(i);
	printf("%lld",ans);
}
发布了867 篇原创文章 · 获赞 55 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/104054101