【题解】BZOJ 1901: Zju2112 Dynamic Rankings

题目传送门(权限题)

一道类似的非权限题

题意

N个数的序列,每次修改一个数或者询问区间里的第K小。可以离线。

简要做法

假如要求在线,可以树状数组套个线段树~

但是这题是可以离线的,就可以乱搞整体二分啦~

非常好写好调。(图文无关)

细节

  • 可以把一个修改拆成+1和-1两个操作
  • 数组要开到30000左右,如果初始数组也当做操作的话

代码

#include <bits/stdc++.h>
#define TR(x) cerr<<#x<<'='<<x<<endl
using namespace std;
const int MAXN=30005, INF=0x3f3f3f3f;
void rd(int &x){
	x=0; int ch=getchar();
	while(ch<'0'||'9'<ch) ch=getchar();
	while('0'<=ch&&ch<='9') x=x*10+ch-'0', ch=getchar();
}
int N, M, nq;
int A[MAXN], c[MAXN], id[MAXN], q1[MAXN], q2[MAXN], ans[MAXN];
void add(int x, int k){for(;x<=N;x+=x&-x)c[x]+=k;}
int sum(int x){int r=0;for(;x;x-=x&-x)r+=c[x];return r;}
struct Qry{
	int t,a,b,k;
	Qry(int t,int a,int b,int k):t(t),a(a),b(b),k(k){}
	Qry(){}
}Q[MAXN];
void solve(int *a, int n, int l, int r){
	if(!n) return;
	if(l==r){
		for(int i=0; i<n; ++i) ans[a[i]]=l;
		return;
	}
	int mid=(l+r)>>1, n1=0, n2=0;
	for(int i=0; i<n; ++i){
		Qry &q=Q[a[i]];
		if(q.t==0){
			if(q.b<=mid) add(q.a,q.k),q1[n1++]=a[i];
			else q2[n2++]=a[i];
		}else{
			int t=sum(q.b)-sum(q.a-1);
			if(q.k<=t) q1[n1++]=a[i];
			else q.k-=t,q2[n2++]=a[i];
		}
	}
	for(int i=0; i<n; ++i){
		Qry &q=Q[a[i]];
		if(q.t==0&&q.b<=mid) add(q.a,-q.k);
	}
	memcpy(a,q1,sizeof(int)*n1);
	memcpy(a+n1,q2,sizeof(int)*n2);
	solve(a,n1,l,mid);
	solve(a+n1,n2,mid+1,r);
}
int main(){
	rd(N),rd(M);
	for(int i=1; i<=N; ++i) rd(A[i]),Q[nq++]=Qry(0,i,A[i],1);
	for(int i=1; i<=M; ++i){
		char o[3]; scanf("%s",o);
		int a,b,k;
		if(o[0]=='Q'){
			rd(a),rd(b),rd(k);
			Q[nq++]=Qry(1,a,b,k);
		}else{
			rd(a),rd(k);
			Q[nq++]=Qry(0,a,A[a],-1);
			Q[nq++]=Qry(0,a,A[a]=k,1);
		}
	}
	for(int i=0; i<nq; ++i) id[i]=i;
	solve(id,nq,0,INF);
	for(int i=0; i<nq; ++i)if(Q[i].t)printf("%d\n",ans[i]);
	return 0;
}
posted @ 2017-04-28 14:52  will7101  阅读(933)  评论(1编辑  收藏  举报