【数据结构】【treap】【poj 2761】Feed the dogs
问题
给定你一个数列,a1~an,其中n<=100005,给定m(M<=50000)个形如“x y k”的询问,让你输出x到y这段区间中第k小的数是多少。
分析
如果用快排,时间复杂度是nmlogn一定会超时,那么只好用二叉排序树进行动态维护,时间复杂度接近mlogn,但是单纯的bst还是很有可能超时,所以用treap维护这棵平衡树。
基于贪心的思想,尽量少的对树进行操作,对区间的首端点排序,之后按顺序对每个区间进行操作,如果当前区间的数不在已构树中,就加入这些新节点,如果树中多出不在区间中的节点则删去。这样操作m次即可。
只需对最基本的平衡树操作多开一个域,记录一个节点的左边有多少儿子即比他小的数有多少个。同理记下比他大的数有多少个,之后如果比他小的数有k-1个,输出当前数,否则递归搜索。
至于在基本操作时,改域的修改方式需要认真研究,避免错误,详见代码:
code
program liukee;
type
tree=^rec;
rec=record
lch:tree;
rch:tree;
lsum:longint;
rsum:longint;
heap:longint;
data:longint;
end;
var
a:array[0..100005] of longint;
x,y,k,ans,wei:array[0..60000] of longint;
root:tree;
left,right,i,j,n,m,anss:longint;
procedure init;
var
i:longint;
begin
readln(n,m);
for i:=1 to n do
read(a[i]);
for i:=1 to m do
begin
readln(x[i],y[i],k[i]);
wei[i]:=i;
end;
end;
procedure qsort(l,r:longint);
var
i,j,mid,temp:longint;
begin
i:=l;j:=r;mid:=x[(l+r)div 2];
repeat
while x[i]<mid do inc(i);
while x[j]>mid do dec(j);
if i<=j then
begin
temp:=x[i];x[i]:=x[j];x[j]:=temp;
temp:=y[i];y[i]:=y[j];y[j]:=temp;
temp:=k[i];k[i]:=k[j];k[j]:=temp;
temp:=wei[i];wei[i]:=wei[j];wei[j]:=temp;
inc(i);
dec(j);
end;
until i>j;
if l<j then qsort(l,j);
if i<r then qsort(i,r);
end;
procedure pig1(var root:tree);
var
temp:tree;
begin
temp:=root^.lch;
root^.lch:=temp^.rch;
if root^.lch<>nil then
root^.lsum:=temp^.rsum
else root^.lsum:=0;
temp^.rch:=root;
temp^.rsum:=root^.lsum+root^.rsum+1;
root:=temp;
end;
procedure pig2(var root:tree);
var
temp:tree;
begin
temp:=root^.rch;
root^.rch:=temp^.lch;
if root^.rch<>nil then
root^.rsum:=temp^.lsum
else
root^.rsum:=0;
temp^.lch:=root;
temp^.lsum:=root^.lsum+root^.rsum+1;
root:=temp;
end;
procedure ins(var root:tree;x:longint);
begin
if root=nil then
begin
new(root);
root^.data:=x;
root^.heap:=random(maxlongint);
root^.lch:=nil;root^.rch:=nil;
root^.lsum:=0;root^.rsum:=0;
end
else
begin
if x<root^.data then
begin
ins(root^.lch,x);
inc(root^.lsum);
if root^.lch^.heap<root^.heap then pig1(root);
end
else
begin
ins(root^.rch,x);
inc(root^.rsum);
if root^.rch^.heap<root^.heap then pig2(root);
end;
end;
end;
Procedure Delete(Var root:tree;x:longint);
Begin
If (root=nil) then exit;
if root^.data=x then
begin
If (root^.lch=nil) or (root^.rch=nil) then
Begin
If root^.rch=nil then root:=root^.lch else root:=root^.rch;
End
Else begin
If root^.lch^.heap<root^.rch^.heap then
Begin
pig1(root);
dec(root^.rsum);
Delete(root^.rch,x);
End
Else begin
pig2(root);
dec(root^.lsum);
Delete(root^.lch,x);
End;
End;
End
else
begin
if x<root^.data then
begin
dec(root^.lsum);
delete(root^.lch,x);
end
else begin
dec(root^.rsum);
delete(root^.rch,x);
end;
end;
End;
procedure dfs(root:tree;k:longint);
begin
if root=nil then exit;
if (root^.lsum=k-1) then
begin
anss:=root^.data;
exit;
end
else if root^.lsum>=k then dfs(root^.lch,k)
else dfs(root^.rch,k-root^.lsum-1);
end;
function max(a,b:longint):longint;
begin
if a>b then exit(a);
exit(b);
end;
begin
randomize;
init;
qsort(1,m);
right:=0;
root:=nil;
x[0]:=x[1];
for i:=1 to m do
begin
for j:=x[i-1] to x[i]-1 do
delete(root,a[j]);
if y[i]<right then
for j:=y[i]+1 to right do
delete(root,a[j]);
if y[i]>right then
begin
for j:=max(right+1,x[i]) to y[i] do
ins(root,a[j]);
right:=y[i];
end;
dfs(root,k[i]);
ans[wei[i]]:=anss;
end;
for i:=1 to m do
writeln(ans[i]);
end.
反思
经典算法,灵活运用。
把每个过程都打对就是ac,一丝不苟!
浙公网安备 33010602011771号