【Nov 1P4,快排、插排还是?】黑匣子
【题目描述】
Black Box是一种原始的数据库。它可以存储一个整数数组,还有一个特别的变量i。最开始的时候Black Box是空的,而i等于0。这个Black Box要处理一串命令。
命令只有两种:
ADD(x):把x元素放进Black Box;
GET:i加1,然后输出Black Box中第i小的数。
记住:第i小的数,就是Black Box里的数按从小到大的顺序排序后的第i个元素。
例如:
我们来演示一下一个有11个命令的命令串。(如下图所示)
现在要求找出对于给定的命令串的最好的处理方法。ADD和GET命令分别最多有200000个。
现在用两个整数数组来表示命令串:
1.A(1),A(2),…A(M):一串将要被放进Black Box的元素。每个数都是绝对不超过2000000000的整数,M≤200000。例如上面的例子就是A=(3,1,-4,2,8,-1000,2)。
2.u(1),u(2),…u(N):表示第u(j)个元素被放进了Black Box里后就出现了一个GET命令。例如上面的例子中的u=(1,2,2,6)。输入数据不用判错。
【输入格式】
第一行,两个整数,M,N。
第二行,M个整数,表示A(1) …A(M)。
第三行,N个整数,表示u(1)…u(N)。
【输出格式】
输出Black Box根据命令串所得出的输出串,一个数字一行。
【输入样例】
7 4
3 1 -4 2 8 -1000 2
1 2 6 6
【输出样例】
3
3
1
2
【数据规模】
对于30%的数据,M≤10000;
对于50%的数据,M≤100000
对于100%的数据,M≤200000
由于要频繁取出第X小值,很容易让人想到堆这种高效的数据结构,事实证明这种想法是正确的,G3的Bensen神牛就用了一种传说中的“一个大根堆+一个小根堆”的神奇方法A掉此题。可是弱小的我在堆里徘徊了一个多小时,最后还是放弃打这条路打了快排…
首先是快排的代码。只要会编程的人都会打,模拟插入的过程一个一个的插入就可以,纯粹的NlogN~但是快排中也有技巧,有的童鞋写的快排能得50分,而我的朴素快排只有30…在这里仅供参考,不再深入探讨。
参考代码(快排,30):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 program blackbox;
2 var
3 tot,p:longint;
4 m,n,i,j:longint;
5 a:array[1..200000]of longint;
6 aa,u:array[1..200000]of longint;
7 procedure qsort(l,r: longint);
8 var
9 i,j,x,y: longint;
10 begin
11 i:=l;
12 j:=r;
13 x:=a[(l+r) div 2];
14 repeat
15 while a[i]<x do
16 inc(i);
17 while x<a[j] do
18 dec(j);
19 if not(i>j) then
20 begin
21 y:=a[i];
22 a[i]:=a[j];
23 a[j]:=y;
24 inc(i);
25 j:=j-1;
26 end;
27 until i>j;
28 if l<j then
29 qsort(l,j);
30 if i<r then
31 qsort(i,r);
32 end;
33 begin
34 readln(m,n);
35 for i:=1 to m do
36 read(aa[i]);
37 for i:=1 to n do
38 read(u[i]);
39 p:=1;
40 tot:=0;
41 j:=0;
42 for i:=1 to m do
43 begin
44 inc(tot);
45 a[tot]:=aa[i];
46 qsort(1,tot);
47 while u[p]=i do
48 begin
49 inc(j);
50 writeln(a[j]);
51 inc(p);
52 end;
53 if p>m then break;
54 end;
55 end.
其实这个过程和插入排序很像…So我打了个插排,以为在一定情况下效率高于快排,但是还是30~
参考代码(插排,30)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 program blackbox;
2 var
3 tot,p:longint;
4 m,n,i,j:longint;
5 a:array[1..200000]of longint;
6 aa,u:array[1..200000]of longint;
7 procedure qsort(x:longint);
8 var
9 i,j:longint;
10 begin
11 i:=1;
12 while(a[i]<=x)and(i<=tot-1)do inc(i);
13 for j:=tot downto i+1 do a[j]:=a[j-1];
14 a[i]:=x;
15 end;
16 begin
17 readln(m,n);
18 for i:=1 to m do
19 read(aa[i]);
20 for i:=1 to n do
21 read(u[i]);
22 p:=1;
23 tot:=0;
24 j:=0;
25 for i:=1 to m do
26 begin
27 inc(tot);
28 qsort(aa[i]);
29 while u[p]=i do
30 begin
31 inc(j);
32 writeln(a[j]);
33 inc(p);
34 end;
35 if p>m then break;
36 end;
37 end.
其实对付这种题还有一个利器——二叉排序树~其实建树的过程就是ADD的过程,这个谁都会,关键是找到第K小值。
我们用一个域表示当前节点的左儿子数。这样从根节点开始,如果k-1比当前节点的左儿子少,就在它的左子树中找,如果大就在右子树中找,相同就说明找到了。这样用一个递归就可以解决问题,编程复杂度很低~
参考代码(二叉排序树,100):
1 program blackbox;
2 type l=record
3 nn:longint; //nn表示这个节点的权
4 l,r,s:longint; //l是左节点,r是右节点,s是左儿子数
5 end;
6 var
7 t,x,y,p:longint;
8 m,n,i,j:longint;
9 a:array[1..200000]of l;
10 aa,u:array[1..200000]of longint;
11 procedure print(x,po:longint);
12 begin
13 if a[po].s=x then
14 begin
15 writeln(a[po].nn);
16 exit;
17 end;
18 if a[po].s>x then print(x,a[po].l); //递归左子树
19 if a[po].s<x then print(x-a[po].s-1,a[po].r); //右子树,要把左边的节点数减掉
20 end;
21 begin
22 readln(n,m);
23 for i:=1 to n do read(aa[i]);
24 for i:=1 to m do read(u[i]);
25 p:=1;
26 for i:=1 to n do
27 begin
28 a[i].nn:=aa[i];
29 t:=1;
30 while i<>1 do //建树
31 begin
32 if a[i].nn>=a[t].nn then
33 begin
34 if a[t].r=0 then
35 begin
36 a[t].r:=i;
37 break;
38 end
39 else t:=a[t].r;
40
41 end;
42 if a[i].nn<a[t].nn then
43 begin
44 if a[t].l=0 then
45 begin
46 a[t].l:=i;
47 inc(a[t].s);
48 break;
49 end
50 else begin
51 inc(a[t].s);
52 t:=a[t].l;
53 end;
54 end;
55 end;
56 while u[p]=i do //输出
57 begin
58 print(p-1,1);
59 inc(p);
60 end;
61 end;
62 end.
(saltless原创,转载请注明出处)