题目
凸优化选手往后稍稍。
显然我们是选择(k)对相邻的,所以我们可以差分后将题意转化为选(k)个不相邻的数使其和最小。
我们考虑一个贪心:每次选最小的。这个贪心的错误性是显然的。
我们加入一个反悔机制:
取走一个数(a)之后,我们把它旁边的两个数(b,c)去掉,在这个位置放一个新的数(b+c-a)。
然后我们用堆维护最小值,用链表维护旁边的数,再开个桶记录某个位置的数是否被删即可。
如何理解这个贪心的正确性呢:如果你不选择一个当前最小的点,那么你是为了选择它旁边的两个点。
自己随便想想就能理解了。
#include<bits/stdc++.h>
using namespace std;
const int N=100007,inf=0x3f3f3f3f;
struct node{int v,p;}t;
int operator<(node a,node b){return a.v>b.v;}
int f[N],l[N],r[N],v[N];
priority_queue<node>q;
void del(int x){l[x]=l[l[x]],r[x]=r[r[x]],r[l[x]]=x,l[r[x]]=x;}
int read(){int x;scanf("%d",&x);return x;}
int main()
{
int n=read(),m=read(),i,ans=0,p;
for(i=0;i<n;++i) v[i]=read();
for(i=n-1;i;--i) v[i]-=v[i-1],l[i]=i-1,r[i]=i+1,q.push((node){v[i],i});
v[0]=v[n]=inf;
for(i=1;i<=m;++i)
{
while(f[q.top().p]) q.pop();
t=q.top(),q.pop(),p=t.p,ans+=t.v,f[l[p]]=f[r[p]]=1,v[p]=v[l[p]]+v[r[p]]-v[p],q.push((node){v[p],p}),del(p);
}
return !printf("%d",ans);
}