【题意】
公司动态排名开发了一种新型计算机,不再满足于简单地找到给定N个数的第k个最小数。他们开发了一个更强大的系统,对于N个数a[1],a[2],…,a[N],你可以问它:a[i],a[i+1],…,a[j]的第k个最小数是多少?(i<=j,0<k<=j-i+1)。你甚至可以改变一些a[i]的值,并继续查询。
你的任务是为这台计算机编写一个程序
- 从输入中读取N个数字(1 <= N <= 50000)
- 处理输入的M指令(1 <= M <= 10000)。这些指令包括查询a[i],a[i+1],…,a[j]的第k个最小数,或将a[i]改为t。
【输入】
输入的第一行是单个数字X(0<X<=4),即输入的测试用例的数量。然后每个X块代表一个测试用例。
每个块的第一行包含两个整数N和M,表示N个数和M个指令。接下来是N行。第i+1行代表数字a[i]。然后是M行,格式如下
Q i j k
或者
C i t
它表示查询a[i],a[i+1],…,a[j]的第k个数,并分别将a[i]变为t。保证在任何操作时间,任何数字a[i]是小于1000000000的非负整数。
两个连续测试用例之间没有空行。
【输出】
对于每个查询操作,输出一个整数来表示结果。(即a[i],a[i+1],…,a[j]的第k个最小数)
两个连续测试用例之间没有空行。
【输入样例】
2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
【输出样例】
3
6
3
6
整体分治模板题
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define g getchar()
using namespace std;
const int N=70010;
void qr(int &x) {
char c=g;x=0;
while(!isdigit(c))c=g;
while(isdigit(c))x=x*10+c-'0',c=g;
}
void write(int x) {
if(x/10)write(x/10);
putchar(x%10+'0');
}
void pri(int x) { write(x); puts("");}
struct rec { int op,x,y,z;}q[N],lq[N],rq[N];//操作序列(按时间排列)
int T,t,p,n,m,a[N],b[N],tot,c[N],ans[N];
//数据总数,操作数,询问数。
int ask(int x) {
int y=0;
for( ;x;x&=x-1) y+=c[x];
return y;
}
int up(int x,int y) {
for( ;x<=n;x+=x&-x)
c[x]+=y;
}
void clear(int x) {
for( ;x&&c[x];x+=x&-x)
c[x]=0;
}
int sta[N],top;
//整体分治其实就是把原问题分成若干个相同的子问题。
//当问题缩小到一定规模时,答案自然水落石出。
void solve(int l,int r,int st,int ed) {
if(st>ed) return ;
if(l==r) {
for(int i=st;i<=ed;i++)
ans[q[i].op]=b[l];
return ;
}
int mid=(l+r)>>1,val=b[mid],lt=0,rt=0;
for(int i=st;i<=ed;i++) {
if(!q[i].op) {
if(q[i].y<=val) up(q[i].x,q[i].z),lq[++lt]=q[i],sta[++top]=q[i].x;
else rq[++rt]=q[i];
}
else {
int cnt=ask(q[i].y)-ask(q[i].x-1);
if(q[i].z<=cnt) lq[++lt]=q[i];
else q[i].z-=cnt,rq[++rt]=q[i];
}
}
while(top) clear(sta[top--]);//还原树状数组
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() {
qr(T);
while(T--) {
qr(n);qr(m);t=p=0;
for(int i=1;i<=n;i++) {
qr(a[i]);
q[++t]=(rec){0,i,a[i],1};
}
memcpy(b,a,(n+1)<<2); tot=n;
for(int i=1;i<=m;i++) {
char s[5];scanf("%s",s);
int l,r,k;qr(l);qr(r);
if(s[0]=='C') {//本题核心思想:把修改拆成 减去原来的数 + 加上现在的数 两个部分
q[++t]=(rec){0,l,a[l],-1};
q[++t]=(rec){0,l,a[l]=r,1};
b[++tot]=r;
}
else qr(k),q[++t]=(rec){++p,l,r,k};
}
sort(b+1,b+tot+1); top=1;
for(int i=2;i<=tot;i++)
if(b[i]!=b[top]) b[++top]=b[i];//答案一定在出现的数中
tot=top; top=0;
solve(1,tot,1,t);
for(int i=1;i<=p;i++) pri(ans[i]);
}
return 0;
}