Jzoj4726 种花

相信大家都猜到题意了,简单说一下

圆形广场共有 N 个种花的位置,顺时针编号1到N。并且每个位置都有一个美观度ai ,两株花不能种在相邻的位置(1号和N号也算相邻位置)一共有 M 株花,现在小D也想知道应该如何摆这 N 株花才能使美观度最大

这道题显然可以用堆,每次将堆顶最大的元素取出并删除两边的元素

但是正确性显然有问题,比如10,11,10,1显然答案是20而不是12

那么每次,我们取出一个节点i后,要向堆里增加一个“撤回”节点,其位置i不变,但是值v[i]变为v[i-1]+v[i+1]-v[i],同时删除i-1,i+1两个节点,这样的话,我们就需要一个双向链表来维护左右节点是否被选择,l[i]表示i左边第一个没有备选的节点,r[i]类似

流程:

1.取出节点(i,v[i])

2.删除l[i],r[i],并将l[l[i]]以及r[r[i]]与i相连

3.向堆里加入节点(i,v[l[i]]+v[r[i]]-v[i]),那么如果取出这个节点,就相当于放弃i,而改为选择l[i]和r[i]

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std;
struct flower{ int id,x; };
inline bool operator < (flower a,flower b){ return a.x<b.x; }
int l[200010],r[200010],d[200010],w[200010],n,m,ans=0;
priority_queue<flower> q;
int main(){
	scanf("%d%d",&n,&m);
	if(n<m*2) return 0&puts("Error!");
	for(int x,i=0;i<n;++i){
		scanf("%d",&x);
		q.push((flower){i,w[i]=x});
		l[i]=(i-1+n)%n;
		r[i]=(i+1)%n;
	}
	for(int i=0,j;i<m;++i){
		flower a=q.top(); q.pop();
		if(d[j=a.id]){ --i; continue; }
		ans+=a.x; q.push((flower){j,w[j]=(w[l[j]]+w[r[j]]-a.x)});
		r[l[l[j]]]=j; l[r[r[j]]]=j;
		d[l[j]]=1; d[r[j]]=1;
		l[j]=l[l[j]]; r[j]=r[r[j]];
	}
	printf("%d
",ans);
}

原文地址:https://www.cnblogs.com/Extended-Ash/p/9477301.html