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\n",ans);
}