Zoo

Description

JZ拥有一个很大的野生动物园。这个动物园坐落在一个狭长的山谷内,这个区域从南到北被划分成N个区域,每个区域都饲养着一头狮子。这些狮子从北到南编号为1,2,3,…,N。每头狮子都有一个觅食能力值Ai,Ai越小觅食能力越强。饲养员西西决定对狮子进行M次投喂,每次投喂都选择一个区间[I,J],从中选取觅食能力值第K强的狮子进行投喂。值得注意的是,西西不愿意对某些区域进行过多的投喂,他认为这样有悖公平。因此西西的投喂区间是互不包含的(即区间[1,10]不会与[3,4]或[5,10]同时存在,但可以与[9,11]或[10,20]一起)。同一区间也只会出现一次。你的任务就是算出每次投喂后,食物被哪头狮子吃掉了。

Input

觅食能力值。(1<=能力值<=maxlongint)。此后M行,每行描述一次投喂。第t+2的三个数I,J,K表示在第t次投喂中,西西选择了区间[I,J]内觅食能力值第K强的狮子进行投喂。

Output

输出文件有M行,每行一个整数。第i行的整数表示在第i次投喂中吃到食物的狮子的觅食能力值。插入代码:
Hint

对于100%的数据,有1<=N<=100000,1<=M<=50000。

分析

这题就是要求区间的第k小数。
这也是很经典的主席树。一维维护区间,另一维维护数值的出现次数。

求主席树的过程
procedure insert(var x:longint;//注意x是变参,因为每次要改变更新root或lef或rig的值
                        y,l,r,s:longint);
var mid:longint;
begin
    inc(tot);
    x:=tot;
    left[x]:=left[y];
    right[x]:=right[y];//左右两边复制
    sum[x]:=sum[y]+1;//更新,统计
    mid:=(l+r)shr 1;
    if l=r then exit;
    if mid>=s then insert(left[x],left[y],l,mid,s)
    else insert(right[x],right[y],mid+1,r,s);//修改其他部分
end;

查询代码(第k小)

function query(l,r,x,y,z:longint):longint;
var mid:longint;
begin
    if l=r then exit(l);
    mid:=(l+r)shr 1;
    if sum[left[y]]-sum[left[x]]>=z then exit(query(l,mid,left[x],left[y],z))
    else exit(query(mid+1,r,right[x],right[y],z-sum[left[y]]+sum[left[x]]));//通过计算区间里面的数的个数来判断这个数在左边还是右边
end;

其实很类似线段树。

代码

var
    a,b,c,d:array[0..1000000] of longint;
    tot,i,n,m,lf,rf,vf,t:longint;
    left,right,sum,root:array[0..15000000] of longint;
procedure qrt(l,r:longint);
var i,j,mid:longint;
begin
    i:=l;j:=r;mid:=a[(l+r)shr 1];
    repeat
        while a[i]<mid do inc(i);
        while a[j]>mid do dec(j);
        if i<=j then begin
            a[0]:=a[i];a[i]:=a[j];a[j]:=a[0];
            b[0]:=b[i];b[i]:=b[j];b[j]:=b[0];inc(i);dec(j);
        end;
    until i>j;
    if l<j then qrt(l,j);
    if i<r then qrt(i,r);
end;
procedure insert(var x:longint;y,l,r,s:longint);
var mid:longint;
begin
    inc(tot);
    x:=tot;
    left[x]:=left[y];
    right[x]:=right[y];
    sum[x]:=sum[y]+1;
    mid:=(l+r)shr 1;
    if l=r then exit;
    if mid>=s then insert(left[x],left[y],l,mid,s)
    else insert(right[x],right[y],mid+1,r,s);
end;
function query(l,r,x,y,z:longint):longint;
var mid:longint;
begin
    if l=r then exit(l);
    mid:=(l+r)shr 1;
    if sum[left[y]]-sum[left[x]]>=z then exit(query(l,mid,left[x],left[y],z))
    else exit(query(mid+1,r,right[x],right[y],z-sum[left[y]]+sum[left[x]]));
end;
begin
    readln(n,m);
    for i:=1 to n do begin
        read(a[i]);
        b[i]:=i;
    end;
    qrt(1,n);a[0]:=0;
    for i:=1 to n do
    if a[i]<>a[i-1] then begin
       inc(t);
       c[i]:=t;
       d[t]:=a[i];
    end else c[i]:=t;
    for i:=1 to n do a[b[i]]:=c[i];
    for i:=1 to n do
    insert(root[i],root[i-1],1,t,a[i]);
    for i:=1 to m do begin
        readln(lf,rf,vf);
        writeln(d[query(1,t,root[lf-1],root[rf],vf)]);
    end;
close(input);close(output);
end.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值