整体二分入门——区间k小数查询

Description

You are working for Macrohard company in data structures department. After failing your previous task about key insertion you were asked to write a new data structure that would be able to return quickly k-th order statistics in the array segment.
That is, given an array a[1…n] of different integer numbers, your program must answer a series of questions Q(i, j, k) in the form: “What would be the k-th number in a[i…j] segment, if this segment was sorted?”
For example, consider the array a = (1, 5, 2, 6, 3, 7, 4). Let the question be Q(2, 5, 3). The segment a[2…5] is (5, 2, 6, 3). If we sort this segment, we get (2, 3, 5, 6), the third number is 5, and therefore the answer to the question is 5.
Input

The first line of the input file contains n — the size of the array, and m — the number of questions to answer (1 <= n <= 100 000, 1 <= m <= 5 000).
The second line contains n different integer numbers not exceeding 109 by their absolute values — the array for which the answers should be given.
The following m lines contain question descriptions, each description consists of three numbers: i, j, and k (1 <= i <= j <= n, 1 <= k <= j - i + 1) and represents the question Q(i, j, k).
Output

For each question output the answer to it — the k-th number in sorted a[i…j] segment.
Sample Input

7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
Sample Output

5
6
3
Hint

This problem has huge input,so please use c-style input(scanf,printf),or you may got time limit exceed.
Source

Northeastern Europe 2004, Northern Subregion

本题就是整体二分的入门题,本题的主要思想就是说
对于序列中的数和询问一起分治,如果l和r相同,那么就是说已经搜到了答案,就把区间内所有的询问答案赋为l,然后就是分治,运用单词询问的思想,先取中间值mid,然后对于小于等于mid的修改处理掉,然后对于每个询问按时间顺序依次判,如果小于等于k就扔到左边,否则mid以前的一定对其造成影响,减去后扔到右边,然后递归左右两边即可出解。
其实就是单次询问的二分思想,就像二叉树检索一样,很直观,然后这次是整体的,就分开来判断。我们用树状数组来维护区间个数大小。
感谢:
http://blog.csdn.net/yukizzz/article/details/51278816
此文代码十分易懂
http://www.cnblogs.com/zig-zag/archive/2013/04/18/3027707.html
此文分析十分透彻

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 300001
#define F(i,a,b) for(int i=a;i<=b;i++)
#define r(x) scanf("%d",&x)
#define pln(x) printf("%d\n",x)
struct query
{
    int x,y,k,id,ty;
};
query xx[N],oned,q1[N],q2[N];
int cnt;
int p1,p2;
int x,y,k,val,n,m;
int bit[N+666],ans[N];
void prepare(void)
{
    r(n);r(m);
    F(i,1,n)
    {
        r(val);++cnt;
        oned.id=i;oned.x=val;oned.ty=1;oned.k=1;
        xx[cnt]=oned;
    }
    F(i,1,m)
    {
        r(x);r(y);r(k);++cnt;
        oned.ty=2;oned.k=k;oned.x=x;oned.y=y;oned.id=i;
        xx[cnt]=oned;
    }   
}
int lowbit(int x)
{
    return (x&(-x));
}
void add(int x,int k)
{
    for(int i=x;i<=n;i+=lowbit(i))bit[i]+=k;
}
int sum(int x)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i))
    {
        res+=bit[i];
    }
    return res;
}
void dance(int h,int t,int l,int r)
{
    if(t<h) return;
    if(l==r)
    {
        for(int i=h;i<=t;i++) 
        if(xx[i].ty==2)
        {
            ans[xx[i].id]=l;
        }
        return;
    }
    int mid=l+r>>1;
    int p1=0;int p2=0;
    for(int i=h;i<=t;i++)
    {
        if(xx[i].ty==1)
        {
            if(xx[i].x<=mid) 
            {
                add(xx[i].id,1);
                q1[++p1]=xx[i];
            }
            else q2[++p2]=xx[i];
        }
        else
        {
            int res=sum(xx[i].y)-sum(xx[i].x-1);
            if(res>=xx[i].k)
            {
                q1[++p1]=xx[i];
            }
            else
            {
                xx[i].k-=res;
                q2[++p2]=xx[i];
            }
        }
    }
    for(int i=1;i<=p1;i++)
    {
        if(q1[i].ty==1) add(q1[i].id,-1);
    }
    int p=h-1;
//  for(int i=1;i<=p1;i++) cout<<q1[i].ty<<" ";puts("");
//  for(int i=1;i<=p2;i++) cout<<q2[i].ty<<" ";puts("");
    for(int i=1;i<=p1;i++) xx[p+i]=q1[i];
    for(int i=1;i<=p2;i++) xx[p+p1+i]=q2[i];
//  for(int i=h;i<=t;i++) cout<<xx[i].ty<<" ";puts("");
    dance(h,p+p1,l,mid);
    dance(p+p1+1,t,mid+1,r);
}
int main(void)
{
    prepare();
    dance(1,cnt,-1e9-1,1e9+1);
    for(int i=1;i<=m;i++)pln(ans[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值