题目大意
%
对于长度为
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
1⩽m,n⩽100000
题解
%
考虑整体二分。
先考虑对每个询问二分答案,可以发现处理每个询问的二分中有很多冗杂操作,每次都要不断二分找到符合答案的区间,考虑能否将若干个操作整体二分。每次二分答案——第
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;
}