BZOJ_1901_Zju2112 Dynamic Rankings_树状数组+主席树

BZOJ_1901_Zju2112 Dynamic Rankings_树状数组+主席树

题意:

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1
],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改
变后的a继续回答上面的问题。
 
分析:
区间第k小,可以用主席树维护
然而有修改,每次修改都会对后面的线段树有影响
因为一般的主席树是前缀和的思想
不妨用树状数组维护可持久化线段树
查询和修改都乘上个logn
 
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 100500
int t[N*40],ls[N*40],rs[N*40],n,m,root[N*40];
int sx[N],sy[N],v[N],maxn=1000000000,cnt,lx,ly;
char ch[10];
void insert(int x,int &y,int l,int r,int val,int c){
	if(!y)y=++cnt;
	if(l==r) { t[y] = t[x] + c; return ;}
	int mid=l+r>>1;
	if(val<=mid) rs[y]=rs[x],insert(ls[x],ls[y],l,mid,val,c);
	else ls[y]=ls[x],insert(rs[x],rs[y],mid+1,r,val,c);
	t[y] = t[ls[y]] + t[rs[y]];
}
int query(int l,int r,int k){
	if(l==r)return l;
	int sizls=0,mid=l+r>>1,i;
	for(i=1;i<=ly;i++) sizls += t[ls[sy[i]]];
	for(i=1;i<=lx;i++) sizls -= t[ls[sx[i]]];
	if(k<=sizls){
		for(i=1;i<=ly;i++) sy[i]=ls[sy[i]];
		for(i=1;i<=lx;i++) sx[i]=ls[sx[i]];
		return query(l,mid,k);
	}else{
		for(i=1;i<=ly;i++) sy[i]=rs[sy[i]];
		for(i=1;i<=lx;i++) sx[i]=rs[sx[i]];
		return query(mid+1,r,k-sizls);
	}
}
int main(){
	scanf("%d%d",&n,&m);
	int i,x,y,z,tot=n,j,k;
	for(i=1;i<=n;i++) { scanf("%d",&v[i]);for(j=i;j<=n;j+=j&(-j))insert(root[j],root[j],0,maxn,v[i],1); }
	for(i=1;i<=m;i++) {
		scanf("%s%d%d",ch,&x,&y);
		if(ch[0]=='C'){
			for(j=x;j<=n;j+=j&(-j)) insert(root[j],root[j],0,maxn,v[x],-1);
			v[x]=y;
			for(j=x;j<=n;j+=j&(-j)) insert(root[j],root[j],0,maxn,v[x],1);
		}else{
			scanf("%d",&z);
			for(lx=0,j=x-1;j;j-=j&(-j)) sx[++lx] = root[j];
			for(ly=0,j=y;j;j-=j&(-j)) sy[++ly] = root[j];
			printf("%d
",query(0,maxn,z));
		}
	}
}
 
原文地址:https://www.cnblogs.com/suika/p/8594278.html