种花

Description

经过三十多个小时的长途跋涉,小Z和小D终于到了NOI现场——南山南中学。一进校园,小D就被花所吸引了(不要问我为什么),遍和一旁的种花园丁交(J)流(L)了起来。
他发现花的摆放竟有如此奥秘:圆形广场共有 NN 个种花的位置,顺时针编号 1  N N。并且每个位置都有一个美观度aiai ,如果在这里种花就可以得到这 ai  ai  的美观度。但由于地处南山土壤肥力欠佳,两株花不能种在相邻的位置( 1  1 号和 N  N 号也算相邻位置)。校方一共给了 MM 株花,经过园丁的精妙摆放,才能如此吸引小D。所以现在小D也想知道应该如何摆这 N 株花。

Input

输入第一行包含两个整数 N,M N,M
接下来一行包含 N  N 个正整数,依次描述美观度 a1,a2,...,an a1,a2,...,an

Output

输出一个整数,表示最佳植树方案可以得到的美观度。如果无解输出Error!“Error!”,不包含引号。

Data Constraint

对于 50%  50% 的数据满足 N<=3000 N<=3000
对于 100%  100% 的数据满足 M<=N<=2000001000<=ai<=1000 M<=N<=200000,−1000<=ai<=1000

Sample Input

7 3
1 2 3 4 5 6 7

Sample Output

15

Solution

贪心+堆。
 Ai  Ai 为关键字建大根堆,用一个链表存放当前物品。
最初链表中元素是 11~Ni  的后继是 i+1i+1,前驱是 i1i−1(当然,11 的前驱是 NNN 的后继是 1)。
执行 MM 次操作,每一次操作都将堆顶元素 k 取出,ans+=Akans+=Ak。然后在链表中删除 kk 的前驱 pre
和后继 nxtnxt,令 Ak=Apre+AnxtAkAk=Apre+Anxt−Ak,并更新堆。
这样做的合理性:
保证了一段区间内的最优解,并增加了撤销性。
(笔者由于一时懵逼打了棵线段树,凑合着用吧)

Code

const   maxn=200005;
var     a:array[1..maxn] of int64;
        suf,pre:array[1..maxn] of longint;
        tree:array[1..5*maxn] of int64;
        n,m,i:longint;
        ans:int64;
function max(x,y:int64):int64;
begin if x>y then exit(x);exit(y);end;
procedure build(x,l,r:longint);
var     mid:longint;
begin
    if l=r then begin
        tree[x]:=a[l];
        exit;
    end;
    mid:=(l+r) div 2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
    tree[x]:=max(tree[x*2],tree[x*2+1]);
end;
procedure kill(x,l,r,z:longint);
var mid:longint;
begin
    if l=r then begin
        tree[x]:=-maxlongint;
        a[l]:=-maxlongint;
                exit;
    end;
    mid:=(l+r) div 2;
    if mid>=z then kill(x*2,l,mid,z) else
    kill(x*2+1,mid+1,r,z);
    tree[x]:=max(tree[x*2],tree[x*2+1]);
end;
procedure work(x,l,r:longint);
var     mid:longint;
begin
    if l=r then begin
        ans:=ans+tree[x];
        tree[x]:=a[pre[l]]+a[suf[l]]-a[l];
        a[l]:=a[pre[l]]+a[suf[l]]-a[l];
        kill(1,1,n,pre[l]);
        kill(1,1,n,suf[l]);
        pre[l]:=pre[pre[l]];
        suf[pre[l]]:=l;
        suf[l]:=suf[suf[l]];
        pre[suf[l]]:=l;
                exit;
    end;
    mid:=(l+r) div 2;
    if tree[x*2]>=tree[x*2+1] then work(x*2,l,mid)
    else work(x*2+1,mid+1,r);
    tree[x]:=max(tree[x*2],tree[x*2+1]);
end;

begin
    readln(n,m);
    read(a[1]);
    pre[1]:=n;
    suf[1]:=2;
    for i:=2 to n-1 do begin
        read(a[i]);
        pre[i]:=i-1;
        suf[i]:=i+1;
    end;
    read(a[n]);
    pre[n]:=n-1;
    suf[n]:=1;
    build(1,1,n);
    if m>n div 2+n mod 2 then begin
        writeln('Error!');
        exit;
    end;
    for i:=1 to m do work(1,1,n);
    writeln(ans);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值