Luogu P2617 Dynamic Rankings

题目大意

%   对于长度为 n n n 的序列,有 m m m 个操作,对于操作1,给出 l , r l,r l,r k k k,求区间 [ l , r ] [l,r] [l,r] k k k 大的数;对于操作2,给出 i i i x x x,将 i i i 位置的值改为 x x x
  数据范围  1 ⩽ m , n ⩽ 100000 1\leqslant m,n\leqslant 100000 1m,n100000

题解

%   考虑整体二分。
  先考虑对每个询问二分答案,可以发现处理每个询问的二分中有很多冗杂操作,每次都要不断二分找到符合答案的区间,考虑能否将若干个操作整体二分。每次二分答案——第 k k k 大的值,然后用树状数组统计区间内大于等于 k k k 的数的个数,若询问的 k k k 大于树状数组统计的答案,则将该询问放在右边,否则放在左侧。
  由于这个过程是和(伪)顺序结构的,只要我们不改变修改操作和查询操作之间的顺序,就不会影响答案的正确性。因而总时间复杂度为 T ( n ) = Θ ( n log ⁡ 2 2 n ) T(n)=\Theta(n\log_2^2n) T(n)=Θ(nlog22n)  这和树状数组套主席树的时间复杂度一致。

#include<bits/stdc++.h>
using namespace std;
#define maxn 200010
#define inf 2147483647
int n;
struct que{
	int op,id,x,y,z;
}q[maxn<<1],q1[maxn<<1],q2[maxn<<1];
int arr[maxn];
void add(int x,int y){
	for(;x<=n;x+=(x&(-x)))
		arr[x]+=y;
}
int Base_sum(int x){
	int ans=0;
	for(;x;x-=(x&(-x)))
		ans+=arr[x];
	return ans;
}
int sum(int l,int r){
	return Base_sum(r)-Base_sum(l-1);
}
int ans[maxn],cnt=0;
void solve(int l=1e-9,int r=1e9,int ql=1,int qr=cnt){
	if(ql>qr) return ;
	if(l==r){
		for(int i=ql;i<=qr;i++)
			if(q[i].op==2) ans[q[i].id]=l;
		return;
	}
	int mid=(l+r)>>1,Q1=0,Q2=0;
	for(int i=ql;i<=qr;i++){
		if(q[i].op==1){
			if(q[i].x<=mid) add(q[i].id,q[i].y)
							,q1[++Q1]=q[i];
			else q2[++Q2]=q[i];
		}else{
			int x=sum(q[i].x,q[i].y);
			if(q[i].z<=x) q1[++Q1]=q[i];
			else q[i].z-=x
				,q2[++Q2]=q[i];
		}
	}
	for(int i=1;i<=Q1;i++)
		if(q1[i].op==1) add(q1[i].id,-q1[i].y);
	for(int i=1;i<=Q1;i++) q[i+ql-1]=q1[i];
	for(int i=1;i<=Q2;i++) q[i+ql+Q1-1]=q2[i];
	solve(l,mid,ql,ql+Q1-1);
	solve(mid+1,r,ql+Q1,qr);
}
int t[maxn];
void change(int a,int b){
	if(t[a]!=0) q[++cnt]=(que){1,a,t[a],-1,0};
	t[a]=b; q[++cnt]=(que){1,a,t[a],1,0};
}
int qs=0;
void query(int a,int b,int c){
	q[++cnt]=(que){2,++qs,a,b,c};
}
char s[10];
int main(){
	int cnt=0,m;
	scanf("%d%d",&n,&m);
	for(int i=1,x;i<=n;i++){
		scanf("%d",&x);
		change(i,x);
	}
	for(int i=1,a,b,c;i<=m;i++){
		scanf("%s",s);
		if(s[0]=='C'){
			scanf("%d%d",&a,&b);
			change(a,b);
		}else{
			scanf("%d%d%d",&a,&b,&c);
			query(a,b,c);
		}
	}
	solve(-1e9,1e9);
	for(int i=1;i<=qs;i++)
		printf("%d\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值