1. 程式人生 > >整體二分——帶修改區間第k大

整體二分——帶修改區間第k大

Description

  給定一個長度為N的已知序列A[i](1<=i<=N),要求維護這個序列,能夠支援以下兩種操作:   1、查詢A[i],A[i+1],A[i+2],…,A[j](1<=i<=j<=N)中,升序排列後排名第k的數。   2、修改A[i]的值為j。   所謂排名第k,指一些數按照升序排列後,第k位的數。例如序列{6,1,9,6,6},排名第3的數是6,排名第5的數是9。

Input

  輸入檔案的第一行包含兩個整數N和M,分別表示序列的長度為N和有M個操作。   接下來的N個不大於10^9正整數,第i個表示序列A[i]的初始值。   然後的M行,每行為一個操作Q i j k 或者C i j分別表示查詢A[i],A[i+1],A[i+2],…,A[j](1<=i<=j<=N)中,升序排列後排名第k的數和修改A[i]的值為j。

Output

  對於每個查詢,輸出一行整數,為查詢的結果。測試資料之間不應有空行。

Sample Input

5 3

3 2 1 4 7

Q 1 4 3

C 2 6

Q 2 5 3

Sample Output

3 6

Hint

【資料範圍】:   20%的資料中,m,n≤100;    40%的資料中,m,n≤1000;    100%的資料中,m,n≤10000。

把修改看成刪除一次插入一次,因為整體二分時自然保證時間連續,我們就可以亂搞。

我們二分答案,對於修改的數k小於等於mid的,修改後歸為左區間,查詢類似。

注意整個操作我們均保證了a[i]的時間在a[i-1]的時間之後,因此修改拆成刪除和插入是可以的,這兩個操作總是一個整體。

#include<bits/stdc++.h>
using namespace std;
const int Maxn=40005;
struct Operator{
	int x,k,type;//-1刪除 1新增 0詢問
	int L,R,index;
}a[Maxn],Left[Maxn],Right[Maxn];
int n,m,cnt,cntq,v[Maxn],ans[Maxn];
struct Tree_Array{
	int c[Maxn];
	#define lowbit(x) (x)&-(x)
	void add(int x,int k){
		for(;x<=n;x+=lowbit(x))c[x]+=k;
	}
	int sum(int x){
		int ret=0;
		for(;x>0;x-=lowbit(x))ret+=c[x];
		return ret;
	}
}bit;
void Solve(int ql,int qr,int al,int ar){
	if(ql>qr)return ;
	if(al==ar){
		for(int i=ql;i<=qr;++i)
			if(a[i].type==0)ans[a[i].index]=al;
		return ;
	}
	int cntl=0,cntr=0,cnt=ql-1,mid=(al+ar)/2;
	for(int i=ql;i<=qr;++i){
		if(a[i].type){
			if(a[i].k<=mid){
				bit.add(a[i].x,a[i].type);
				Left[++cntl]=a[i];
			}else {
				Right[++cntr]=a[i];
			}
		}else {
			int Rank=bit.sum(a[i].R)-bit.sum(a[i].L-1);
			if(a[i].k<=Rank){
				Left[++cntl]=a[i];
			}else {
				a[i].k-=Rank;
				Right[++cntr]=a[i];
			}
		}
	}
	for(int i=ql;i<=qr;++i)
		if(a[i].type&&a[i].k<=mid)bit.add(a[i].x,-a[i].type);
	for(int i=1;i<=cntl;++i)a[++cnt]=Left[i];
	for(int i=1;i<=cntr;++i)a[++cnt]=Right[i];
	Solve(ql,ql+cntl-1,al,mid),Solve(ql+cntl,qr,mid+1,ar);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&v[i]);
		a[++cnt]=(Operator){i,v[i],1};
	}
	for(int i=1;i<=m;++i){
		char c=getchar();while(!isalpha(c))c=getchar();
//		cout<<c<<"\n";
		if(c=='Q'){
			a[++cnt].type=0;a[cnt].index=++cntq;
			scanf("%d%d%d",&a[cnt].L,&a[cnt].R,&a[cnt].k);
		}else {
			int x,k;scanf("%d%d",&x,&k);
			a[++cnt]=(Operator){x,v[x],-1};
			a[++cnt]=(Operator){x,v[x]=k,1};
		}
	}
	Solve(1,cnt,-(1<<30),(1<<30));
	for(int i=1;i<=cntq;++i)
		cout<<ans[i]<<endl;
	return 0;
}