题目大意: 有一个圆环状的广场,不能在相邻的位置种树,每个位置种树有一个美观度,现在要求美观度最大。
题解
考虑贪心。
每次取美观度最大的位置即可,但是要考虑一种需要反悔的情况——取 x x x 旁边的两个位置比取 x x x 更优。那么只需要在取了 x x x 之后,往堆里面加一个 v a l [ p r e ] + v a l [ n e x t ] − v a l [ x ] val[pre]+val[next]-val[x] val[pre]+val[next]−val[x], p r e pre pre 和 n e x t next next 是指前驱和后继。同时,还需要将堆里面的 v a l [ p r e ] val[pre] val[pre] 和 v a l [ n e x t ] val[next] val[next] 删掉,相当于把 p r e , x , n e x t pre,x,next pre,x,next 三个点合并成一个点。
代码如下(手写堆有点丑……):
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 200010
int n,m;
struct node{int x,y;};
struct dd{
node a[maxn];
int t;
dd():t(0){}
void up(int x)
{
while(x>1&&a[x].x>a[x/2].x)
{
swap(a[x],a[x/2]);
x/=2;
}
}
void add(int x,int y)
{
a[++t]=(node){x,y};
up(t);
}
void down(int x)
{
if(x>t/2)return;
int ans=x;
if(a[x*2].x>a[x].x)ans=x*2;
if(x*2+1<=t&&a[x*2+1].x>a[ans].x)ans=x*2+1;
if(x!=ans)
{
swap(a[x],a[ans]);
down(ans);
}
}
node pop()
{
node re=a[1];
a[1]=a[t--];
down(1);
return re;
}
bool empty(){return t==0;}
}dui;
int a[maxn],next[maxn],pre[maxn];
bool del[maxn];
int main()
{
scanf("%d %d",&n,&m);
if(m>n/2)return printf("Error!"),0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),dui.add(a[i],i),next[i]=i+1,pre[i]=i-1;
pre[1]=n;next[n]=1;//这是个环
int ans=0;
while(m--)
{
while(del[dui.a[1].y])dui.pop();
node now=dui.pop();
ans+=now.x;
a[now.y]=a[next[now.y]]+a[pre[now.y]]-a[now.y];
del[next[now.y]]=del[pre[now.y]]=true;
next[now.y]=next[next[now.y]];pre[now.y]=pre[pre[now.y]];//更新前驱后继
pre[next[now.y]]=next[pre[now.y]]=now.y;
dui.add(a[now.y],now.y);
}
printf("%d",ans);
}