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