【BOI2011】medians___贪心

206 篇文章 0 订阅
45 篇文章 0 订阅

题目大意:

让我们定义A 为1, 2, 3,。。。, 2 * N - 1 的一个全排列。
定义数列B 为A 的前缀的中位数形成的数列:B[i] 为A[1],A[2],。。。,A[2 * i - 1] 的中位数。
注:对于M 个数的中位数(M 是奇数),可以通过排序后取中间的数得到。
给出N 和数列B。找到一个全排列A 使得前缀中位数形成的数列恰好为B。

• 1 <= A[i] <= 2 * N - 1,对于任意i 从1 到2 * N - 1
• 1 <= B[i] <= 2 * N - 1,对于任意i 从1 到N
• 1 <= N <= 100 000
• 60% 的数据有N <= 1000

题解:

据说是权值线段树,不过呢,可以贪心。
啥,可以贪心?没错。
我们发现因为是一定有解的,所以我们分类讨论:
对于一个给出的bi:
(1)bi在A中已经出现过:
    ①bi>b[i-1]:
    则输出2个能放的最大的数,因为题目满足一定有解,且你大于中位数,所以要放数使得它左移
    ②bi<b[i-1]:
    则输出2个能放的最小的数,原因同①
    ③bi=b[i-1]:
    则输出1个能放的最大的数与1个能放的最小的数,这样使得它继续平衡,从而使得中位数不变
(2bi还没有在A中出现过:
          先输出bi 没有bi这个数哪来的中位数?
     然后对于一个bi,
    ①bi>b[i-1]:
    则放一个当前还能放的最大的数
    因为你在前2*(i-1)个数的中位数b[i-1]的右边,你要使得它平衡你的bi才能成为中位数
    ②bi<b[i-1]:
    则放一个当前还能放的最小的数
    因为你在前2*(i-1)个数的中位数b[i-1]的左边,你要使得它平衡你的bi才能成为中位数
    ③bi=b[i-1]:
      你的bi都没出现过,有可能么?
    时间复杂度:O(2n-1)

代码:

Download 
var
      f:array [0..200001] of boolean;
      a:array [0..100001] of longint;
      l,r,n,m,i,j:longint;

begin
     assign(input,'medians.in'); reset(input);
     assign(output,'medians.out');rewrite(output);
      readln(n);
      read(a[1]);
      write(a[1],' ');
      f[a[1]]:=true;
      l:=1; r:=2*n-1;
      for i:=2 to n do
        begin
             read(a[i]);
             if f[a[i]] then
                begin
                      if a[i]>a[i-1] then
                         begin
                             write(r,' ');
                             f[r]:=true;
                             while f[r] do dec(r);
                             write(r,' ');
                             f[r]:=true;
                             while f[r] do dec(r);
                         end
                      else if a[i]<a[i-1] then
                           begin
                                 write(l,' ');
                                 f[l]:=true;
                                 while f[l] do inc(l);
                                 write(l,' ');
                                 f[l]:=true;
                                 while f[l] do inc(l);
                           end
                      else begin
                                 write(l,' ',r,' ');
                                 f[l]:=true;
                                 f[r]:=true;
                                 while f[l] do inc(l);
                                 while f[r] do dec(r);
                           end;
                end
             else begin
                       f[a[i]]:=true;
                       while f[l] do inc(l);
                       while f[r] do dec(r);
                       write(a[i],' ');
                       if a[i]>a[i-1]
                          then begin
                                     write(r,' '); 
                                     f[r]:=true;
                                     while f[r] do dec(r);
                               end
                          else begin
                                     write(l,' ');
                                     f[l]:=true;
                                     while f[l] do inc(l);
                               end;
                  end;
        end;
      close(input); close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值