P2617 Dynamic Rankings(主席树模板题)

题目链接

题目大意:

对于一个长度为n的数列a,你需要高效地维护如下两种操作:
1、修改,将某个数a[i]改为指定的值t
2、查询,询问区间[i,j]中第k小的数字是多少


前置知识:树状数组,权值线段树

动态找第k小。

树套树的题中,最外的树一般是树状数组,复杂度必线段树低,而且好写。
这种情况下,最外层一般是单点修改(如,这题是修改指定值),里面可以用区间查询

但是如果外面也要区间修改,树状数组也可以写,但写法变得复杂,(支持区间修改的树状数组比较繁琐),不比线段树套线段树有章法。
和这篇是线段树套线段树的题做比较:bzoj1513 [POI2006]Tet-Tetris 3D(二维线段树,永久标记和延迟标记)

第二遍写这题,还是不熟练,多刷题吧。


完整代码:

#include <bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
char getch(){
	char ch=getchar();
	while(ch!='Q' && ch!='C')	ch=getchar();
	return ch;
}
const int maxn=2e5+7;
int n,m,cnt,c,a[maxn],b[maxn<<1],t[maxn];
int rt[maxn*400],L[maxn*400],R[maxn*400],sum[maxn*400];
int op[maxn],x[maxn],y[maxn],z[maxn];
int lc,rc,tl[maxn],tr[maxn];
int lowbit(int x){	return x&(-x);	}
void update(int &i,int pre,int l,int r,int x,int v){
	i=++cnt;	L[i]=L[pre];	R[i]=R[pre];
	sum[i]=sum[pre]+v;
	if(l==r)	return;
	int mid=l+r>>1;
	if(x<=mid)	update(L[i],L[pre],l,mid,x,v);
	else		update(R[i],R[pre],mid+1,r,x,v);
}
int query(int l,int r,int k){
	if(l==r)	return l;
	int mid=l+r>>1,ans=0;
	for(int i=1;i<=lc;i++)	ans-=sum[L[tl[i]]];
	for(int i=1;i<=rc;i++)	ans+=sum[L[tr[i]]];
	if(k<=ans){
		for(int i=1;i<=lc;i++)	tl[i]=L[tl[i]];
		for(int i=1;i<=rc;i++)	tr[i]=L[tr[i]];
		return query(l,mid,k);
	}
	else{
		for(int i=1;i<=lc;i++)	tl[i]=R[tl[i]];
		for(int i=1;i<=rc;i++)	tr[i]=R[tr[i]];
		return query(mid+1,r,k-ans);
	}
}
void modify(int x,int v){
	int k=lower_bound(b,b+c,a[x])-b;
	for(int i=x;i<=n;i+=lowbit(i))	update(rt[i],rt[i],0,c-1,k,v);
}
int ask(int l,int r,int k){
	lc=rc=0;
	for(int i=l-1;i;i-=lowbit(i))	tl[++lc]=rt[i];
	for(int i=r;i;i-=lowbit(i))		tr[++rc]=rt[i];
	return query(0,c-1,k);
}
int main(){
	c=n=read();	m=read();
	for(int i=1;i<=n;i++)	b[i-1]=a[i]=read();
	for(int i=0;i<m;i++){
		char ch=getch();	x[i]=read();	y[i]=read();
		if(ch=='C'){
			op[i]=1;
			b[c++]=y[i];
		}
		else	z[i]=read();
	}
	sort(b,b+c);	c=unique(b,b+c)-b;
	for(int i=1;i<=n;i++)	modify(i,1);
	for(int i=0;i<m;i++){
		if(op[i]){
			modify(x[i],-1);
			a[x[i]]=y[i];
			modify(x[i],1);
		}
		else	printf("%d\n",b[ask(x[i],y[i],z[i])]);
	}
}
/*
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3


3
6
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值