# poj2104 K-th Number（整体二分+树状数组）

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 10 9 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

[Submit]   [Go Back]   [Status]   [Discuss]

• 确定一个区间以及这个区间对应的答案范围
• 确定当前的判断标准M=（L+R）>>1
• 将整个序列中的操作分成两个序列：q1—>[L,M]，q2—>[M+1,R]
• 继续二分

### 操作分类

for (int i=1;i<=n;i++)
{
scanf("%d",&x);
tot++;
q[tot].x=x; q[tot].type=1; q[tot].id=i;
}
//x：数值  type：操作类型  id：在数组中的位置

for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&k);
tot++;
q[tot].x=x; q[tot].y=y; q[tot].k=k; q[tot].type=2; q[tot].id=i;
}
//x：询问左端点 y：询问右端点 k：查找第k小 type：操作类型 id：询问编号

### 整体二分

solve(1,tot,-INF,INF);
//序列左端点 序列右端点 二分的答案左端点 二分的答案右端点

#### First

if (ql>qr) return;  //一定要加，避免死循环
if (L==R)
{
for (int i=ql;i<=qr;i++)
if (q[i].type==2) ans[q[i].id]=L;
//记录答案
return;
} 

#### Second

if (q[i].type==1)
{
if (q[i].x<=M) {
q1[++t1]=q[i];
}
else q2[++t2]=q[i];
}

• x<=M
此问题要求的是区间第k小的数值，所以这种情况下x会给排名产生贡献，我们就在x的位置id上标记1，同时归入第一个序列q1
（实际上这也是一种变向的“左区间修改，右区间询问”）
• x>M
这种情况下x不会给排名产生贡献，我们直接归入第二个序列q2
int tt=ask(q[i].y)-ask(q[i].x-1);
if (tt>=q[i].k) q1[++t1]=q[i];
else{
q[i].k-=tt;
q2[++t2]=q[i];
}

#### Third

for (int i=1;i<=t1;i++)
for (int i=1;i<=t1;i++) q[ql+i-1]=q1[i];
for (int i=1;i<=t2;i++) q[ql+t1+i-1]=q2[i];

#### Fourth

solve(ql,ql+t1-1,L,M);
solve(ql+t1,qr,M+1,R); 

//这里写代码片
#include<cstdio>
#include<iostream>
#include<cstring>

using namespace std;

const int INF=1e9+7;
const int mN=100010;
const int mM=10010;
struct node{
int x,y,k,type,id;
};
node q[mN+mM],q1[mN+mM],q2[mN+mM];
int t[mN],ans[mN],n,m,tot=0;

void add(int x,int z) {for (int i=x;i<=n;i+=(i&(-i))) t[i]+=z;}
int ask(int x) {int ans=0;for (int i=x;i>0;i-=(i&(-i))) ans+=t[i];return ans;}

void solve(int ql,int qr,int L,int R)
{
if (ql>qr) return;
if (L==R)
{
for (int i=ql;i<=qr;i++)
if (q[i].type==2) ans[q[i].id]=L;
return;
}

int M=(L+R)>>1;    //二分的答案
int t1=0,t2=0;
for (int i=ql;i<=qr;i++)
{
if (q[i].type==1)
{
if (q[i].x<=M) {
q1[++t1]=q[i];
}
else q2[++t2]=q[i];
}
else
{
if (tt>=q[i].k) q1[++t1]=q[i];
else{
q[i].k-=tt;
q2[++t2]=q[i];
}
}
}

for (int i=1;i<=t1;i++)
for (int i=1;i<=t1;i++) q[ql+i-1]=q1[i];
for (int i=1;i<=t2;i++) q[ql+t1+i-1]=q2[i];

solve(ql,ql+t1-1,L,M);
solve(ql+t1,qr,M+1,R);
}

int main()
{
scanf("%d%d",&n,&m);
int x,y,k;
for (int i=1;i<=n;i++)
{
scanf("%d",&x);
tot++;
q[tot].x=x; q[tot].type=1; q[tot].id=i;
}
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&k);
tot++;
q[tot].x=x; q[tot].y=y; q[tot].k=k; q[tot].type=2; q[tot].id=i;
}

solve(1,tot,-INF,INF);
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
} 

04-29 90

09-26 313
10-21 175
04-10 125
05-04 239
08-27 1131
07-14 56
08-12 25
05-09 74
10-08 25
04-07 540
01-09 1027