洛谷P1484 种树(反悔贪心,双向链表+堆)

https://www.luogu.com.cn/problem/P1484

最近这个“反悔贪心”出现频率有点儿高。。。

如果选2堆:
如果\(a[x]\)是最大的,那么\(a[x-1]\)\(a[x+1]\)要么都不选,要么扔掉\(a[x]\)之后同时选。
证明:
假设没选最大的\(a[x]\),选了\(a[x-1]\)而没选\(a[x+1]\)
设选的另一个是\(a[k]\)\(a[k]\)\(a[x]\)一定不相邻,选\(a[x]\)\(a[k]\)一定更优

所以就可以选出最大的\(a[x]\)之后,删除掉\(a[x-1]\)\(a[x+1]\),然后修改\(a[x]\)\(a[x-1]+a[x+1]-a[x]\),这样当第二次选择\(a[x]\)时,即表示不选\(a[x]\)而改选\(a[x-1]\)\(a[x+1]\),也就是实现反悔的过程。

用双向链表和堆实现

#include<cstdio>
#include<queue>
#include<iostream>

using namespace std;

#define N 500002

long long a[N];
struct node
{
	int g;
	bool operator < (node p) const
	{
		return a[g]<a[p.g];
	}
}e[N];
priority_queue<node>q;

bool tra[N];
int l[N],r[N];

int main()
{
//	freopen("data.txt","r",stdin);
	int n,k;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;++i)  scanf("%lld",&a[i]);
	for(int i=2;i<=n;++i) l[i]=i-1;
	for(int i=1;i<n;++i) r[i]=i+1;
	node now;
	for(int i=1;i<=n;++i) 
	{
		now.g=i;	
		q.push(now);
	}
	long long ans=0;
	while(k && !q.empty())
	{
		now=q.top();
		q.pop();
		if(a[now.g]<=0) break;
		if(tra[now.g]) continue;
		ans+=a[now.g];		
		--k;
		tra[l[now.g]]=true;
		tra[r[now.g]]=true;
		a[now.g]=a[l[now.g]]+a[r[now.g]]-a[now.g];
		l[now.g]=l[l[now.g]];
		r[l[now.g]]=now.g;
		r[now.g]=r[r[now.g]];
		l[r[now.g]]=now.g;
		q.push(now);
	}
	printf("%lld",ans);
}
posted @ 2021-04-07 21:11  TRTTG  阅读(154)  评论(0编辑  收藏  举报