【洛谷P1484】种树【堆】【贪心】

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P1484
nn个点,选择其中互不相邻的不超过mm个点,使得点权和最大。


思路:

思维题。
考虑贪心。如果我们第一次选择了最大值点ii,那么选择两个点时,要么点i1i-1和点i+1i+1一起选,要么两个都不选。

如果我们选择点i1i-1和点x(xi+1)x(x≠i+1),那么贪心思想,a[i1]+a[x]a[i-1]+a[x]必然大于任意a[q]+a[p]a[q]+a[p]。但是当p=i,q=xp=i,q=x时,a[i>1]+a[x]a[i-> 1]+a[x]肯定小于a[i]+a[x]a[i]+a[x](因为一开始a[i]a[i]是最大的)。矛盾。
证毕

如果不选择a[i1]a[i-1]a[i+1]a[i+1],那么就是一个正常的贪心。但是如果选择a[i1]a[i-1]a[i+1]a[i+1]的话,就需要满足可撤销。
如果我们把a[i]a[i]的值更改成a[i1]+a[i+1]a[i]a[i-1]+a[i+1]-a[i],再次选择aa数组中最大值,那么ansans就等于a[i]+a[i1]+a[i+1]a[i]=a[i1]+a[i+1]a[i]+a[i-1]+a[i+1]-a[i]=a[i-1]+a[i+1]

这样用堆维护最大值,就可以做到O(mlogn)O(mlog n)的复杂度


代码:

#include <queue>
#include <cstdio>
#define mp make_pair
using namespace std;
typedef long long ll;

const int N=500010;
int n,m;
bool p[N];
ll a[N],ans;
priority_queue<pair<ll,int> > q;

struct node
{
	int l,r;
}link[N];

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		q.push(mp(a[i],i));
		link[i].l=i-1; link[i].r=i+1;
	}
	while (m--)
	{
		while (p[q.top().second]) q.pop();
		if (q.top().first<0) break;
		ans+=q.top().first;
		int id=q.top().second;
		a[id]=a[link[id].l]+a[link[id].r]-a[id];
		p[link[id].l]=p[link[id].r]=1;
		link[id].l=link[link[id].l].l; link[link[id].l].r=id;
		link[id].r=link[link[id].r].r; link[link[id].r].l=id;
		q.pop(); q.push(mp(a[id],id));
	}
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998151.html