【数据结构】【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,一丝不苟!

posted @ 2011-04-15 17:00  liukee  阅读(566)  评论(1编辑  收藏  举报