整体二分

简介

顾名思义,将所有操作放在一起进行二分,每一个查询的二分过程都有重叠的部分,整体二分就是一次处理这些部分,达到优化复杂度的效果

详细来说,整体二分的核心代码如下:

void solve(int l,int r,int x,int y)//l,r:值域 x,y:操作序列{
	if(l==r) {for(int i=x;i<=y;i++) if(!q[i].k) ans[q[i].id]=b[l];return;}//二分完成
	for(int i=x;i<=y;i++){
		if(!q[i].k){
			int res=ask(q[i].r)-ask(q[i].l-1);
			if(res>=q[i].v) L.push_back(q[i]);
			else q[i].v-=res,R.push_back(q[i]);//确定在答案在值域左侧还是右侧
		}else{
			if(q[i].v<=mid) add(q[i].l,q[i].k),L.push_back(q[i]);
			else R.push_back(q[i]);//按mid将操作分开
		}
	}
	int tmp=L.size(),tmp1=R.size();
	for(int i=x;i<=y;i++) if(q[i].k&&q[i].v<=mid) add(q[i].l,-q[i].k);//还原
	for(int i=0;i<tmp;i++) q[i+x]=L[i];
	for(int i=0;i<tmp1;i++) q[x+tmp+i]=R[i];
	L.clear(),R.clear();
	solve(l,mid,x,x+tmp-1),solve(mid+1,r,x+tmp,y);//分别二分
}

每个操作/查询最多出现 \(O(\log n)\) 次,再加上树状数组,复杂度 \(O(n \log^2 n)\)

模版

题目

带修区间第 \(k\) 小,看似整体二分这样的离线算符不可做,但事实上我们把修改拆分为添加和删除,按时间排序加入操作序列中,然后再整体二分即可,由于整体二分只看值,因此这样能够保证时间顺序不乱

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
#define mid ((l+r)>>1)
int a[N],n,m,ans[N],tr[N],tot,b[N<<2],tot2;
struct que{int op,l,r,v;}c[N];
struct node{int l,r,v,k,tim,id;}q[N<<2];
vector<node> L,R;
void add(int x,int v){for(;x<=n;x+=(x&-x)) tr[x]+=v;}
int ask(int x){int res=0;for(;x;x-=(x&-x)) res+=tr[x];return res;}
void solve(int l,int r,int x,int y){
	if(l==r) {for(int i=x;i<=y;i++) if(!q[i].k) ans[q[i].id]=b[l];return;}
	for(int i=x;i<=y;i++){
		if(!q[i].k){
			int res=ask(q[i].r)-ask(q[i].l-1);
			if(res>=q[i].v) L.push_back(q[i]);
			else q[i].v-=res,R.push_back(q[i]);
		}else{
			if(q[i].v<=mid) add(q[i].l,q[i].k),L.push_back(q[i]);
			else R.push_back(q[i]);
		}
	}
	int tmp=L.size(),tmp1=R.size();
	for(int i=x;i<=y;i++) if(q[i].k&&q[i].v<=mid) add(q[i].l,-q[i].k);
	for(int i=0;i<tmp;i++) q[i+x]=L[i];
	for(int i=0;i<tmp1;i++) q[x+tmp+i]=R[i];
	L.clear(),R.clear();
	solve(l,mid,x,x+tmp-1),solve(mid+1,r,x+tmp,y);
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){cin>>a[i];b[++tot]=a[i];}
	for(int i=1;i<=m;i++){
		char op;cin>>op;
		if(op=='Q') {c[i].op=1;cin>>c[i].l>>c[i].r>>c[i].v;}
		else {c[i].op=2;cin>>c[i].l>>c[i].v;b[++tot]=c[i].v;}
	}
	sort(b+1,b+tot+1),tot=unique(b+1,b+tot+1)-b-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+tot+1,a[i])-b,q[++tot2]={i,0,a[i],1,0,0};
	for(int i=1;i<=m;i++)
		if(c[i].op==1) q[++tot2]={c[i].l,c[i].r,c[i].v,0,i,i};
		else{
			c[i].v=lower_bound(b+1,b+tot+1,c[i].v)-b;
			q[++tot2]={c[i].l,0,a[c[i].l],-1,i,0},q[++tot2]={c[i].l,0,c[i].v,1,i,0},a[c[i].l]=c[i].v;
		}
	sort(q+1,q+tot2+1,[](node x,node y){return x.tim<y.tim;});
	solve(1,tot,1,tot2);
	for(int i=1;i<=m;i++) if(c[i].op==1) cout<<ans[i]<<"\n";
	return 0;
}
posted @ 2026-10-30 14:50  Rainesxiao  阅读(5)  评论(0)    收藏  举报