题目大意:有一个序列有n个数字,有m个询问,每次询问区间[l,r]第K小的数值是哪一个。
解法:主席树裸题,但这里不用主席树,用整体二分离线来做。
整体二分的思路是二分答案区间,然后将答案在答案区间左边的操作放左边,答案在答案区间右边的操作放右边,并递归处理左答案区间的操作和右答案区间的操作。
具体建议百度搜索一下整体二分学习一波。
当二分到答案区间长度为1时,取出此时操作序列里所有的操作,这些操作的答案就是当前二分到的数值。
#include<iostream>
using namespace std;
#define lowbit(i) (i & (-i))
#include<stdio.h>
const int inf = 1e9;
const int maxn = 1e6 + 10;
struct node {
int val,l,r,op;
}q[maxn],lq[maxn],rq[maxn];
int lt,rt,n,m,ans[maxn],sum[maxn];
void modify(int p,int v) {
for(; p <= maxn; p += lowbit(p)) sum[p] += v;
}
int ask(int p) {
int res = 0;
for(; p; p -= lowbit(p)) res += sum[p];
return res;
}
void solve(int l,int r,int st,int ed) {
if(st > ed || l > r) return;
if(l == r) {
for(int i = st; i <= ed; i++) {
if(q[i].op)
ans[q[i].op] = l;
}
return ;
}
int mid = (l + r >> 1),lt = 0,rt = 0; //这里不要用全局变量,不然下面还会改变
for(int i = st; i <= ed; i++) {
if(q[i].op == 0) {
if(q[i].val <= mid) {
modify(q[i].l,1);
lq[++lt] = q[i];
}
else rq[++rt] = q[i];
}
else {
int cnt = ask(q[i].r) - ask(q[i].l - 1);
if(cnt >= q[i].val) {
lq[++lt] = q[i];
}
else {
q[i].val -= cnt;
rq[++rt] = q[i];
}
}
}
for(int i = 1; i <= lt; i++) {
if(lq[i].op == 0) {
modify(lq[i].l,-1);
}
}
for(int i = 1; i <= lt; i++) {
q[st + i - 1] = lq[i];
}
for(int i = 1; i <= rt; i++) {
q[st + lt + i - 1] = rq[i];
}
solve(l,mid,st,st + lt - 1);
solve(mid + 1,r,st + lt,ed);
}
int main() {
scanf("%d%d",&n,&m);
int x,l,r;
int tot = 0;
for(int i = 1; i <= n; i++) {
scanf("%d",&x);
q[++tot] = {x,i,0,0};
}
for(int i = 1; i <= m; i++) {
scanf("%d%d%d",&l,&r,&x);
q[++tot] = {x,l,r,i};
}
solve(-inf,inf,1,tot);
for(int i = 1; i <= m; i++) {
printf("%d\n",ans[i]);
}
return 0;
}