poj2828

很容易想到一种动态的做法:平衡树……

或者是二分+树状数组

但,前者编程复杂度较大,而且据说会被卡(没试过);后者理论上超时(据说可以擦边过?);

所以要尝试新的算法;

倒着考虑,显然最后一个对象的位置是最容易确定,顺带着,容易发现,

在第i个人后插入,就是在当前队列中前面有i个空位的空位置;

于是还是可以用平衡数……

当然更简单快捷的方法是线段树,每个节点表示这个区间还有的空位数;

然后类比平衡树的查找k大,很快就能写成

 1 var tree:array[0..600010] of longint;
 2     p,a,w:array[0..600010] of longint;
 3     n,i:longint;
 4 
 5 procedure build(i,l,r:longint);
 6   var m:longint;
 7   begin
 8     tree[i]:=r-l+1;
 9     m:=(l+r) shr 1;
10     if l<>r then
11     begin
12       build(i*2,l,m);
13       build(i*2+1,m+1,r);
14     end;
15   end;
16 
17 function ask(i,l,r,k:longint):longint;
18   var m:longint;
19   begin
20     if l=r then
21     begin
22       tree[i]:=0;
23       exit(l);
24     end;
25     m:=(l+r) shr 1;
26     if tree[i*2]>k then
27     begin
28       dec(tree[i*2]);      //在查找的过程中顺便把区间空位数更新
29       exit(ask(i*2,l,m,k));
30     end
31     else begin
32       dec(tree[i*2+1]);
33       exit(ask(i*2+1,m+1,r,k-tree[i*2]));   //有没有觉得很平衡树找区间k值神似
34     end;
35   end;
36 
37 begin
38   while not eoln do
39   begin
40     readln(n);
41     fillchar(tree,sizeof(tree),0);
42     build(1,1,n);
43     for i:=1 to n do
44       readln(p[i],a[i]);
45     for i:=n downto 1 do
46       w[i]:=ask(1,1,n,p[i]);     //w代表每个点的位置
47     for i:=1 to n do
48       p[w[i]]:=a[i];
49     for i:=1 to n do
50     begin
51       write(p[i]);
52       if i<>n then write(' '); //注意别PE了
53     end;
54     writeln;
55   end;
56 end.
View Code
原文地址:https://www.cnblogs.com/phile/p/4473279.html