[BZOJ2006][NOI2010]超级钢琴

2A的题,因为又忘开64位了……不应该啊不应该。不过我还是不明白算术上溢为什么是WA而不是RE

这道题呢在洛谷上有一个神奇的题号2048.

思路:

我们考虑所有可能连续区间的开头下标,可以发现它们是1n-L+1,而对于一个区间左端点x,所有可能的区间右端点是x+L-1x+R-1(如果x+R-1>n别忘了特判)。

我们考虑一个左端点x及右端点范围[y,z],初始时显然y=x+L-1,z=x+R-1。下面,找出一个属于[y,z]的右端点t,使这一范围内[x,t]是和最大的一个区间。(设sum[i]1-i的前缀和,可以发现sum[t]就是sum[y]sum[z]中最大的一个,[x,t]区间的和为sum[t]-sum[x-1]

下面我们把[x,t]区间取出来,ansans+sum[t]-sum[x-1],剩下来两部分新的区间:

1、左端点为x,右端点由y(t-1)

2、左端点为x,右端点由(t+1)z

下面重点来了:

开始时,我们考虑所有的可能,也就是左端点取遍1n-L+1,得到(n-L+1)组如上面所说的x[x+L-1,x+R-1]。假如我们用ST表来维护sum的最大值的话,每一组端点对答案的贡献,也就是上面讲的sum[t]-sum[x-1],以及t本身,都可以在O(1)时间内得到。这样,我们可以每次从当前所有的组中选取贡献最大的一个,更新答案,再将它“分裂”为两个,重复k次,就得到了答案。为了实现,我们可以以sum[t]-sum[x-1]这个值为关键字建立大根堆,就可以实现每次删除一个,插入两个的操作了。时间复杂度O((n+k)logn)

类似的堆的题还是不少的,不过基本都比这水,大致思路都是把所有可能放在堆里,然后不停更新。

program bzoj2006;
const maxn=500010;
type heappoint=record x,l,r,m:longint;s:int64; end;
var n,k,ll,rr,i,tmp,size,j:longint;
    data:array[0..maxn] of longint;
    sum:array[0..maxn] of int64;
    st:array[0..maxn,0..20] of longint;
    pow:array[0..20] of longint;
    log:array[0..2*maxn] of longint;
    temp:heappoint;
    heap:array[0..2*maxn] of heappoint;
    ans:int64;
procedure swap(var x,y:heappoint);
var temp:heappoint;
begin
   temp:=x; x:=y; y:=temp;
end;
function min(x,y:longint):longint;
begin
  if x<y then exit(x) else exit(y);
end;
function findmax(l,r:longint):longint;
begin
  if sum[st[l,log[r-l+1]]]>sum[st[r-pow[log[r-l+1]]+1,log[r-l+1]]] then
    exit(st[l,log[r-l+1]]) else exit(st[r-pow[log[r-l+1]]+1,log[r-l+1]]);
end;
procedure join(x:heappoint);
var i:longint;
begin
  inc(size);
  heap[size]:=x;
  i:=size;
  while (i>1)and(heap[i].s>heap[i shr 1].s) do
    begin
      swap(heap[i],heap[i shr 1]);
      i:=i shr 1;
    end;
end;
procedure del(x:longint);
var i:longint;
begin
  heap[x]:=heap[size];
  dec(size);
  while x shl 1<=size do
    begin
      if x shl 1=size then i:=size
        else if heap[x shl 1].s>heap[x shl 1+1].s then i:=x shl 1
          else i:=x shl 1+1;
      if heap[i].s>heap[x].s then
        begin
          swap(heap[i],heap[x]);
          x:=i;
        end else exit;
    end;
end;
begin
  readln(n,k,ll,rr);
  for i:=1 to n do readln(data[i]);
  sum[0]:=0;
  for i:=1 to n do sum[i]:=sum[i-1]+data[i];
  pow[0]:=1;
  for i:=1 to 20 do
    begin
      pow[i]:=pow[i-1] shl 1;
      if pow[i]>n then break;
    end;
  for i:=1 to 20 do
    begin
      if pow[i]=0 then break;
      for j:=pow[i-1] to pow[i]-1 do log[j]:=i-1;
    end;
  for i:=1 to n do st[i,0]:=i;
  for j:=1 to 20 do
    begin
      if pow[j]>n then break;
      for i:=1 to n do
        if (i+pow[j-1])>n then st[i,j]:=st[i,j-1]
          else if sum[st[i,j-1]]>sum[st[i+pow[j-1],j-1]] then st[i,j]:=st[i,j-1]
            else st[i,j]:=st[i+pow[j-1],j-1];
    end;
  size:=0;
  for i:=1 to n-ll+1 do
    begin
      temp.x:=i;
      temp.l:=i+ll-1;
      temp.r:=min(i+rr-1,n);
      temp.m:=findmax(temp.l,temp.r);
      temp.s:=sum[temp.m]-sum[i-1];
      join(temp);
    end;
  ans:=0;
  for i:=1 to k do
    begin
      ans:=ans+heap[1].s;
      temp:=heap[1];
      tmp:=heap[1].m;
      if tmp>temp.l then
        begin
          temp.r:=tmp-1;
          temp.m:=findmax(temp.l,tmp-1);
          temp.s:=sum[temp.m]-sum[temp.x-1];
          join(temp);
        end;
      temp:=heap[1];
      if tmp<temp.r then
        begin
          temp.l:=tmp+1;
          temp.m:=findmax(temp.l,temp.r);
          temp.s:=sum[temp.m]-sum[temp.x-1];
          join(temp);
        end;
      del(1);
    end;
  writeln(ans);
end.

 

posted @ 2016-05-02 17:30 lkmcfj 阅读(...) 评论(...) 编辑 收藏