题目:找第k小的数
题目描述给出一个长度为N的序列A1,A2,A3,...,AN,其中每项都是
小于10^5的自然数。
现在有M个询问,每个询问都是Ai...Aj中第k小的数等
于多少。
数据范围:
在60%的数据中,1≤N≤1000,1≤M≤1000
在100%的数据中,1≤N≤10000,1≤M≤2000
Darkmaster说:“这题水吧?水了就得给我AC了看看,呵呵!”
输入格式
第一行两个正整数N,M。
第二行N个数,表示序列A1,A2,...,AN。
紧着的M行,每行三个正整数i,j,k(k≤j-i+1),表示
询问Ai...Aj中第k小的数等于多少。
输出格式
共输出M行,第i行输出第i个询问的答案。
==================================================================
按题目给的数据规模,目测记下元素下标跑个快排然后朴素(O(nm+nlog2n))就能过,但作者显然不是这个意思,求区间kth数有一类现成的数据结构。
比如这题,我们可以归并树,其基本思想就是用log2n层的检索,记下归并排序的过程,每次查找区间[l,r]的kth数时,设x=kth,我们可以算出x在区间[l,r]的排名,然后二分枚举即可;至于怎么求x在[l,r]内的排名,可以将[l,r]拆解为若干个子区间(子区间存在于归并的过程中),将每个子区间内小于x的元素数量累加起来即可;至于怎么在子区间内累加,由于子区间是有序的,再来一次二分就可以了。
综上,归并树的空间复杂度为 nlog2n ,时间复杂度为 (log2n)^3。
如果用归并树来实现这一问题的话,当数据规模n<=10^6时可以在1s内出解。
==================================================================
先贴个代码:
var
g:array[1..20,1..10000] of longint;
a:array[1..10000] of longint;
n,m:longint;
procedure fsetup(x,l,r:longint);
var mid,i,j,k:longint;
begin
if l=r then g[x,l]:=a[l]
else begin
mid:=(l+r)shr 1;
fsetup(x+1,l,mid);
fsetup(x+1,mid+1,r);
j:=l; k:=mid+1;
for i:=l to r do
if (k<=r)and((j>mid)or(g[x+1,k]<g[x+1,j])) then begin
g[x,i]:=g[x+1,k];
inc(k);
end else begin
g[x,i]:=g[x+1,j];
inc(j);
end;
end;
end;
function xth(x,l,r,v:longint):longint;
var mid:longint;
begin
if l>r then exit(0);
mid:=(l+r)shr 1;
if g[x,mid]<v then xth:=mid-l+1+xth(x,mid+1,r,v)
else xth:=xth(x,l,mid-1,v)
end;
function th(x,ll,rr,l,r,v:longint):longint;
var mid:longint;
begin
if (ll=l)and(rr=r)then exit(xth(x,l,r,v))
else begin
mid:=(ll+rr)shr 1;
if r<=mid then exit(th(x+1,ll,mid,l,r,v));
if l>mid then exit(th(x+1,mid+1,rr,l,r,v));
exit(th(x+1,ll,mid,l,mid,v)+th(x+1,mid+1,rr,mid+1,r,v));
end;
end;
function query(l,r,k:longint):longint;
var low,high,mid:longint;
begin
low:=0;
high:=100000;
while low<high do begin
mid:=((low+high)shr 1)+1;
if th(1,1,n,l,r,mid)<k then low:=mid
else high:=mid-1;
end;
exit(low);
end;
procedure main;
var i,p,q,r:longint;
begin
readln(n,m);
for i:=1 to n do read(a[i]); readln;
fsetup(1,1,n);
for i:=1 to m do begin
readln(p,q,r);
writeln(query(p,q,r));
end;
end;
begin
main;
end.
==================================================================
应当指出,由于题目限定了元素的取值范围是非负的,所以在对x的二分上直接采用了shr,而在另外一道类似题目上(数据取值范围是-Inf~Inf),笔者采取同样的方式处理结果挂的很惨(把符号位直接挪了过来~囧)。
其实还有一种和归并树很相似的数据结构-划分树,这个等我A掉另外那道题再说吧!