洛谷1552 BZOJ2809 APIO2012 派遣 左偏树

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/89082131

题目链接

题意:
给你一棵 n n 个点的有根树,每个点有两个权值。给你一个 m m ,你要在这 n n 个点中选出一个点,使得从子树中任意选出若干个点(不一定要选根,也不一定要连通),这些点的第一类权值之和不超过 m m ,要让用这些点的个数乘选出的子树的根节点的第二类权值的积最大,求这个最大乘积。 n < = 1 e 5 n<=1e5

题解:
这个题有很多做法,显然是可以线段树合并的,但是我现在要学左偏树,听同学推荐了这个题。

这个题的话左偏树思维难度感觉还是比线段树合并稍微大一点的。主要问题是,你用左偏树的话合并的时间和空间复杂度都是对的,但是你查询要一个一个拿出来再一个一个放回去,这个过程可能会变成 O ( n ) O(n) 的。

然而其实也不怎么难想解决方法。我们不难发现在当前时刻不可能被用到的点,再合并进来一个左偏树之后那些点还是不可能被用到。所以我们的思路是维护一个大根堆,然后记录堆中元素总和,每次删最大的,直到堆中元素之和不超过 m m ,顺便再记录一个堆中的元素个数就行了。

这样就做完了。复杂度 O ( n l o g n ) O(nlogn) 的,空间上是 O ( n ) O(n) 的,比线段树合并优秀。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,fa[200010],c[200010],l[200010],f[200010],ch[200010][2],dis[200010];
int hed[200010],cnt,rt,sz[200010];
long long ans,sum[200010];
struct node
{
	int to,next;
}a[400010];
inline int read()
{
	int x=0;
	char s=getchar();
	while(s>'9'||s<'0')
	s=getchar();
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x;
}
inline void add(int from,int to)
{
	a[++cnt].to=to;
	a[cnt].next=hed[from];
	hed[from]=cnt;
}
inline int getr(int x)
{
	if(x==f[x])
	return x;
	else
	{
		f[x]=getr(f[x]);
		return f[x];
	}
}
inline int merge(int x,int y)
{
	if(!x||!y)
	return x+y;
	if(c[x]<c[y])
	swap(x,y);
	ch[x][1]=merge(ch[x][1],y);
	f[x]=x;
	f[ch[x][0]]=x;
	f[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][1]]+1;
	return x; 
}
inline int pop(int x)
{
	c[x]=-1;
	f[ch[x][0]]=ch[x][0];
	f[ch[x][1]]=ch[x][1];
	f[x]=merge(ch[x][0],ch[x][1]);
	return f[x];
}
inline void dfs(int x)
{
	sz[x]=1;
	for(int i=hed[x];i;i=a[i].next)
	{
		int y=a[i].to;
		dfs(y);
		sz[x]+=sz[y];
		int fx=getr(x),fy=getr(y);
		merge(fx,fy);
		fx=getr(x);
		sum[x]+=sum[y];
		while(sum[x]>m)
		{
			sum[x]-=c[getr(fx)];
			sz[x]--;
			fx=pop(fx);
		}
	}
	ans=max(ans,1ll*sz[x]*l[x]);
}
int main()
{
	n=read();
	m=read();
	for(int i=1;i<=n;++i)
	{
		fa[i]=read();
		c[i]=read();
		l[i]=read();
		sum[i]=c[i];
		f[i]=i;
		if(fa[i])
		add(fa[i],i);
		else
		rt=i;
	}
	dfs(rt);
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/89082131
今日推荐