您的位置:首页 > 理论基础 > 数据结构算法

第k大区间和问题的树状数组实现

2012-09-28 20:08 197 查看

问题描述:

    给定一个整数序列a[1..N],定义sum[i][j]=a[i]+a[i+l]+……+a[j],将所有的sum[i][j]从小到大排序(其中i,j满足1<=i<=j<=N),得到一个长为N*(N+1)/2的序列,求该序列中的第k个元素。

输入格式(ktm.in)

    第一行有两个整数N,k,其中0<N<=20000,1<=k<=N*(N+1)/2,数据保证任何一个sum[i][j]的绝对值不超过2^30。

    接下来N行每行一个整数。顺序给出序列a的元素。

输出格式(kth.out)                       

   sum序列中的第k个元素

题解:

      这道题据说是noip难度的,如果noip真的考这个我就可以直接退役了.

      我讲这道题主要是想讲如何用树状数组来代替平衡树的部分功能,从而节省代码量.

      算法是很显然的:首先二分答案.假设答案为k,我们求出所有sum中小于k的个数就行了.算法的瓶颈在于什么找到所有sum中有多少小于k.

      先讲讲平衡树的做法:预处理一个s数组,s[i]表示sum[0,i].依次加入s[i],统计s1~s[i-1]中大于s[i]-k的个数即可.利用一个平衡树就可以很简单的做到这些操作.

      然而,平衡树不仅代码量较大且常数很高,所以我们考虑利用其他数据结构来实现这一功能.

      这个数据结构要支持两种操作:加入:加入一个元素;查找:查找所有元素中比k小的元素个数.

      这个东西貌似只有平衡树之类的高级数据结构能做,但是,我们可以只用一个树状数组就实现这些功能.

      我们先将所有的s值排序.首先按原先的次序依次加入si.先将插入操作.插入时直接将si当前对应的下标置为1即可(初值为0).而加入si前,我们要查找在si之前的元素中大于si-k的个数.我们可以先在s中二分出一个p,使s[p]之前的元素都大于si-k,然后求出1~p中的前缀和即可.前缀和可以用树状数组维护.

     这个方法虽然多了一个二分操作和一个排序.但是排序相信大家都能在5分钟内搞出来.至于二分也非常简单.而树状数组的代码量远小于平衡树,并且几乎不会打错,所以这个方法还是很实用的.至于时间复杂度,虽然算法多了一个二分的复杂度log(n),但不要忘记,树状数组的常数远小于平衡树,空间消耗也非常小.所以这种处理方式不会差与平衡树,甚至在某些情况下比平衡树更优.

    下面是我的代码

program kth;
type
int=longint;
var
i,j,k,m,n:int;
s,f,p,rank:array[0..100000]of int;
x,y,z:int;

procedure swap(var x,y:int);var t:int;
begin
t:=x;x:=y;y:=t;
end;

procedure sort(l,r:int);var i,j:int;
begin
i:=l;j:=r;
x:=s[l+random(r-l)];
repeat
while s[i]>x do inc(i);
while s[j]<x do dec(j);
if i<=j then begin
swap(s[i],s[j]);swap(rank[i],rank[j]);
inc(i);dec(j);
end;
until i>j;
if i<r then sort(i,r);
if j>l then sort(l,j);
end;

function ask(x:int):int;var i:int;
begin
ask:=0;i:=1;
while i<=x do begin
while i+(i and -i)<=x do i:=i+(i and -i);
inc(ask,f[i]);inc(i);
end;
end;

procedure ins(x:int);
begin
while x<=n do begin
inc(f[x]);x:=x+(x and -x);
end;
end;

function get(min:int):int;var l,r,mid,i:int;
begin
l:=1;r:=n;
while r-l>3 do begin
mid:=(l+r)>>1;
if s[mid]>=min then l:=mid
else r:=mid;
end;
for i:=l to r do if(s[i-1]>=min)and(s[i]<min)then exit(i-1);
exit(n);
end;

function sum(max:int):int;var i,j,k:int;
begin
sum:=0;
fillchar(f,sizeof(f),0);
for i:=1 to n do begin
j:=p[i];k:=get(s[j]-max);
inc(sum,ask(k));ins(j);
end;
end;

function ans:int;var l,r,mid:int;
begin
l:=s
-s[1];r:=s[1]-s
;
while true do begin
mid:=trunc((l+r)/2);
if sum(mid)>=m then r:=mid
else l:=mid;
if r-l<10 then begin
for i:=l-1 to r do begin
k:=sum(i);
if k>=m then exit(i);
end;
end;
end;
end;

begin
read(n,m);s[0]:=0;
for i:=1 to n do begin
read(x);s[i]:=s[i-1]+x;rank[i]:=i+1;
end;
inc(n);s
:=0;rank
:=1;
sort(1,n);s[0]:=maxlongint;
for i:=1 to n do p[rank[i]]:=i;
write(ans);
end.


BY QW
转载请注明出处
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构 算法 random