两种操作,第一种操作是求区间[i,j]的第k小数,第二种操作是修改某个值
把整个数组划分为长度为根号n(大概223)的块,每个块内排序,查询时二分答案,然后两端的块直接遍历,中间的块二分查找,复杂度根号n
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int SIZE=223;
int a[100100];
int b[100100];
int n,m;
int find(int l,int r,int x) {
if (b[l]>x) return 0;
int s=l;
while (l!=r) {
int t=(l+r)/2+1;
if (b[t]<=x) l=t;
else r=t-1;
}
return l-s+1;
}
int getnum(int l,int r,int t) {
int ans=0,i;
for (i=l;i%SIZE!=1&&i<=r;i++) {
if (a[i]<=t) ans++;
}
for (;i+SIZE<=r;i+=SIZE) {
ans+=find(i,i+SIZE-1,t);
}
for (;i<=r;i++) {
if (a[i]<=t) ans++;
}
//printf("%d %d %d %d\n",l,r,t,ans);
return ans;
}
void print(int a[]) {
for (int i=1;i<=n;i++) printf("%d ",a[i]);
printf("\n");
}
int main() {
int tt,x,y,z,i;
char c;
scanf("%d",&tt);
while (tt--) {
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++) {
scanf("%d",&a[i]);
b[i]=a[i];
}
for (i=1;i<=n;i+=SIZE) {
sort(b+i,b+min(i+SIZE,n+1));
}
//print(a);
//print(b);
for (i=0;i<m;i++) {
scanf(" %c",&c);
if (c=='Q') {
scanf("%d%d%d",&x,&y,&z);
int l=0,r=1000000100;
while (l!=r) {
int t=(l+r)/2;
if (getnum(x,y,t)<z) l=t+1;
else r=t;
}
printf("%d\n",l);
} else {
scanf("%d%d",&x,&y);
a[x]=y;
int l=x,r=x;
for (;l%SIZE!=1;l--);
for (;r%SIZE!=0&&r<n;r++);
for (int j=l;j<=r;j++) b[j]=a[j];
sort(b+l,b+r+1);
}
//print(a);
//print(b);
}
}
return 0;
}