整体二分
蒟蒻对整体二分的初步理解:
这是用来解决一类区间询问的问题
最最经典的就是求区间kth
这是个离线算法,将询问一起处理
过程有点类似CDQ,但并不一样
我们假定当前询问或修改是[L,R](不一定对应原操作序列)
他们的答案值域(或修改值)都在[range_l, range_r]
考虑如何二分成[range_l, mid]与[mid+1, range_r]
如果对于一个询问,在它询问的区间的数中,<=mid的个数小于kth
那么显然答案应该在右区间,反之在左区间
那我们应该如何求 <=mid的个数 ?
一种方法就是用树状数组维护(of course,优秀的线段树也可以胜任)
如果\(a_i\)<=mid,就在树状数组\(tr_i\)位置上加1
所以我们开始时将序列转化为树状数组上的“加”操作
如果遇到修改,我们就可以将原数从树状数组上减去,再添加上当前数的“加”操作
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
const int inf=1e9;
using namespace std;
inline int reads()
{
int sign=1,re=0; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') sign=-1; c=getchar();}
while('0'<=c&&c<='9'){re=re*10+(c-'0'); c=getchar();}
return sign*re;
}
struct operation
{
int l,r,k,op,id;
// 当op=1时表示修改,l表示修改的值,r表示+1/-1,k无意义,id表示修改的位置
// 当op=2时表示询问,[l,r]表示查询的区间,k表示第kth,id表示答案储存的位置(输出顺序)
}q[300005],ql[300005],qr[300005]; int cnt;
int n,m,a[100005];
int out_cnt,ans[100005];
namespace BIT
{
int tr[100005];
inline int lb(int i){return i&(-i);}
inline void add(int now,int add)
{
while(now<=n)
{
tr[now]+=add;
now+=lb(now);
}
}
inline int query(int now)
{
int re=0;
while(now)
{
re+=tr[now];
now^=lb(now);
}
return re;
}
} using namespace BIT;
void dimid(int range_l,int range_r,int L,int R)
{
if(L>R)return;
if(range_l==range_r)
{
for(int i=L;i<=R;i++)
if(q[i].op==2) ans[q[i].id]=range_l;
return;
}
int mid=(range_l+range_r)>>1,cntl=0,cntr=0;
for(int i=L;i<=R;i++)
{
if(q[i].op==1)
{
if(q[i].l<=mid) add(q[i].id,q[i].r),ql[++cntl]=q[i];
else qr[++cntr]=q[i];
}
else
{
int sum=query(q[i].r)-query(q[i].l-1);
if(q[i].k<=sum) ql[++cntl]=q[i];
else q[i].k-=sum,qr[++cntr]=q[i];
}
}
for(int i=1;i<=cntl;i++)
if(ql[i].op==1) add(ql[i].id,-ql[i].r); // 记得将树状数组清零!
for(int i=1;i<=cntl;i++) q[L+i-1]=ql[i];
for(int i=1;i<=cntr;i++) q[L+cntl-1+i]=qr[i];
dimid(range_l,mid,L,L+cntl-1); dimid(mid+1,range_r,L+cntl,R);
}
int main()
{
n=reads(); m=reads();
for(int i=1;i<=n;i++)
{
a[i]=reads();
q[++cnt]=(operation){a[i],1,0,1,i};
}
for(int i=1;i<=m;i++)
{
char c=getchar();
while(c!='Q'&&c!='C') c=getchar();
int l=reads(),r=reads();
if(c=='Q')
{
int kth=reads();
q[++cnt]=(operation){l,r,kth,2,++out_cnt};
}
else // 修改一分为二
{
q[++cnt]=(operation){a[l],-1,0,1,l};
q[++cnt]=(operation){a[l]=r,1,0,1,l};
}
}
dimid(0,inf,1,cnt);
for(int i=1;i<=out_cnt;i++) printf("%d\n",ans[i]);
return 0;
}