jzoj P2152【2017.7.7普及】终极数

71 篇文章 0 订阅
13 篇文章 0 订阅

题目大意:
给定一个长度为n的序列a,试求出对于序列a的每一个前缀的终极数x,使得
这里写图片描述
最小,试求出终极数t(如若有多个终极数t,只需输出最小的那个)

题解:
虽然看似很迷,其实就是每次插入后的中位数中排序后的中位数:
我们维护两个堆来处理,一个大根堆,一个小根堆,当前每次加入一个数,必须保证
小根堆的个数大于等于大根堆的个数,可以理解为——第一次加入数的时候肯定是放
到最小堆。
其次,我们还需保证小根堆里的每个值必须大于等于大根堆当中的最大值,这样就可以使得每次小根堆的堆顶必定是当前的中位数。
然后每次插入任意一个堆中的数,就要维护,然后判断2个堆的堆顶
堆顶/小 < 堆顶/大 这时候很明显我们需要替换,然后替换后分别维护一下,维护后继续判断,直到满足小根堆根大根堆的定义以及满足小根堆的每个值都大于等于大根堆得最大值。

最后将记录下来的中位数排序输出最终的中位数,即终极数。

var
   smbi:array [1..2,0..1000001] of longint;
   b:Array [0..1000001] of longint;
   n,i,j,k1,k2,d1,d2,x:longint;


procedure qsort(l,r:longint);
var
   i,j,mid:longint;
begin
   if l>=r then exit;
   i:=l; j:=r;
   mid:=b[(l+r) div 2];
   repeat
       while b[i]<mid do inc(i);
       while b[j]>mid do dec(j);
       if i<=j then
       begin
           b[0]:=b[i];
           b[i]:=b[j];
           b[j]:=b[0];
           inc(i); dec(j);
       end;
   until i>j;
   qsort(i,r);
   qsort(l,j);
end;

procedure down(x,y:longint);
var
   t:longint;
begin
      if y*2>smbi[x,0] then exit;
      if x=1 then
      repeat
           y:=y*2;
           if y<smbi[x,0] then
              if smbi[x,y]<smbi[x,y+1] then inc(y);
           if smbi[x,y div 2]<smbi[x,y]
              then begin
                       t:=smbi[x,y];
                       smbi[x,y]:=smbi[x,y div 2];
                       smbi[x,y div 2]:=t;
                   end
              else break;
      until y*2>smbi[x,0];

      if x=2 then
      repeat
           y:=y*2;
           if y<smbi[x,0] then
              if smbi[x,y]>smbi[x,y+1] then inc(y);
           if smbi[x,y div 2]>smbi[x,y]
              then begin
                       t:=smbi[x,y];
                       smbi[x,y]:=smbi[x,y div 2];
                       smbi[x,y div 2]:=t;
                   end
      until y*2>smbi[x,0];
end;

procedure up(x,y:longint);
var
   k,t:longint;
begin
   if y=1 then exit;
   if x=1 then
   repeat
       if smbi[x,y]>smbi[x,y div 2]
          then begin
                 t:=smbi[x,y];
                 smbi[x,y]:=smbi[x,y div 2];
                 smbi[x,y div 2]:=t;
               end
          else break;
       y:=y div 2;
   until y=1;

   if x=2 then
   repeat
       if smbi[x,y]<smbi[x,y div 2]
          then begin
                   t:=smbi[x,y];
                   smbi[x,y]:=smbi[x,y div 2];
                   smbi[x,y div 2]:=t;
               end
          else break;
       y:=y div 2;
   until y=1;

end;


procedure check;
var
    i:longint;
begin
       i:=smbi[2,1];
       smbi[2,1]:=smbi[1,1];
       smbi[1,1]:=i;
end;

begin
    assign(input,'c.in');reset(input);
    assign(output,'c.out'); rewrite(output);
    readln(n);
    smbi[1,0]:=0; smbi[2,0]:=0;
    k1:=1; k2:=1; d1:=1; d2:=1;
    for i:=1 to n do
    begin
         read(x);
         if i mod 2<>0 then begin
                                  if smbi[2,0]<k1
                                      then smbi[2,0]:=smbi[2,0]+1
                                      else begin smbi[2,0]:=smbi[2,0]+1; d1:=d1*2; k1:=k1+d1 end;
                                  smbi[2,smbi[2,0]]:=x;
                                  up(2,smbi[2,0]);
                            end
                       else begin
                                  if smbi[1,0]<k2
                                      then smbi[1,0]:=smbi[1,0]+1
                                      else begin smbi[1,0]:=smbi[1,0]+1; d2:=d2*2; k2:=k2+d2 end;
                                  smbi[1,smbi[1,0]]:=x;
                                  up(1,smbi[1,0]);
                            end;
         while smbi[2,1]<smbi[1,1] do
         begin
               check;
               down(2,1);
               down(1,1);
         end;
         b[i]:=smbi[2,1];
    end;
    qsort(1,n);
    writeln(b[n div 2]);
    close(input); close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值