整体二分

蒟蒻对整体二分的初步理解:

这是用来解决一类区间询问的问题

最最经典的就是求区间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;
}
posted @ 2022-03-10 13:48  zuytong  阅读(21)  评论(0编辑  收藏  举报