logo AlgoBeat OnlineJudge
登录 注册

整体二分

2026-06-03 20:57:04 编辑于 2026-06-28 22:22:33 115 0

核心思想

整体二分,顾名思义就是在整体上二分。

可以使用整体二分解决的题目需要满足以下性质:
询问的答案具有可二分性。
修改对判定答案的贡献互相独立,修改之间互不影响效果。
修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值。
贡献满足交换律,结合律,具有可加性
题目允许使用离线算法。
——许昊然《浅谈数据结构题几个非经典解法》

与其说整体二分是一种二分,不如说它是一种分治。
设函数 表示询问 的答案在区间 中。将该区间 划分为 两个子区间,利用数据结构处理 ,并将询问分成 两组子询问。与线段树建树相同的方式递归处理

经典例题

例 1 洛谷 【模板】可持久化线段树 2

给定 个整数构成的序列 ,将对于指定的闭区间 查询其区间内的第 小值。

先考虑 时怎么做。
不难想到单次询问二分第 小值,通过 来获得答案。
设值域大小为 ,时间复杂度

考虑满分做法。
题目允许离线,且答案单调,考虑整体二分做法。
我们设 表示 对应的原询问编号, 表示关于询问 ,在当前值域左侧已经确定的、比答案小的数字的总个数。
表示询问 的答案在 中。函数内部,我们将 的数染黑,利用树状数组结合 计算询问区间内黑点个数,递归处理左右区间。
::::info[时间复杂度证明]{open} 通过观察,发现 的的移动次数为 。由于函数内部套了一层树状数组,总时间复杂度应乘上一只 。又因为每层遍历了一次 ,所以总时间复杂度为 。 ::::

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+7;
int n;
struct BIT{
	int tr[N];
	int lowbit(int x){
		return x&-x;
	}
	void add(int x,int c){
		while(x<=n){
			tr[x]+=c;
			x+=lowbit(x);
		}
	}
	int query(int x){
		int res=0;
		while(x>0){
			res+=tr[x];
			x-=lowbit(x);
		}
		return res;
	}
}bit;
int cas;
struct node{
	int v,id;
	bool operator<(node a)const{
		return v<a.v;
	}
}a[N];
struct Q{
	int l,r,k;
}q[N];
int q1[N],q2[N],id[N],ans[N],cur[N];
void Solve(int l,int r,int ql,int qr){
	if(ql>qr)return;
	if(l==r){
		for(int i=ql;i<=qr;i++)ans[id[i]]=a[l].v;
		return;
	}
	int mid=(l+r)/2;
	for(int i=l;i<=mid;i++)bit.add(a[i].id,1);
	int cnt1=0,cnt2=0;
	for(int i=ql;i<=qr;i++){
		int sum=cur[id[i]]+bit.query(q[id[i]].r)-bit.query(q[id[i]].l-1);
		if(sum>=q[id[i]].k)q1[++cnt1]=id[i];
		else q2[++cnt2]=id[i],cur[id[i]]=sum;
	}
	for(int i=l;i<=mid;i++)bit.add(a[i].id,-1);
	int qcnt=ql-1;
	for(int i=1;i<=cnt1;i++)id[++qcnt]=q1[i];
	for(int i=1;i<=cnt2;i++)id[++qcnt]=q2[i];
	Solve(l,mid,ql,ql+cnt1-1);
	Solve(mid+1,r,ql+cnt1,qr);
}
void solve(){
	//start
	cin.tie(0)->ios::sync_with_stdio(0);
	cout.tie(0);
	cin>>n>>cas;
	for(int i=1;i<=n;i++){
		cin>>a[i].v;
		a[i].id=i;
	} 
	sort(a+1,a+n+1);
	for(int i=1;i<=cas;i++)cin>>q[i].l>>q[i].r>>q[i].k,id[i]=i;
	Solve(1,n,1,cas);
	for(int i=1;i<=cas;i++)cout<<ans[i]<<'\n';
}
signed main(){
	solve();
	return 0;
}

例 2 洛谷 [国家集训队] 矩阵乘法

给你一个 的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第 小数。

考虑 的数据。
不难想到暴力二分,遍历矩阵,计算 的数的个数。
为值域上界,总体时间复杂度为

考虑 的数据。
发现与普通区间 kth 的区别在于这题的 是二维的。
同样,我们可以将 存成一维数组形式,利用二维树状数组来整体二分。
表示询问 的答案在 中(此处 指一维数组)。函数内部,我们将 的数染黑,利用二维树状数组结合 计算询问区间内黑点个数,递归处理左右区间。
由于二维树状数组单次修改查询是 的,所以时间复杂度为

#include<bits/stdc++.h>
using namespace std;
const int N=507,M=6e4+7;
int tr[N][N],id[M],n;
int lowbit(int x){
	return x&-x;
}
void add(int x,int y,int c){
	for(int i=x;i<=n;i+=lowbit(i)){
		for(int j=y;j<=n;j+=lowbit(j)){
			tr[i][j]+=c;
		}
	}
}
int query(int x,int y){
	int res=0;
	for(int i=x;i>0;i-=lowbit(i)){
		for(int j=y;j>0;j-=lowbit(j)){
			res+=tr[i][j];
		}
	}
	return res;
}
struct node{
	int x,y,v;
	bool operator<(node a)const{
		return v<a.v;
	}
}mat[N*N];
struct Q{
	int x_1,y_1,x_2,y_2,k; 
}q[M];
int cas,cur[M],q1[M],q2[M],ans[M];
int get(int x,int y){
	return (x-1)*n+y;
}
void Solve(int l,int r,int ql,int qr){
	if(ql>qr)return;
	if(l==r){
		for(int i=ql;i<=qr;i++)ans[id[i]]=mat[l].v;
		return;
	}
	int mid=(l+r)>>1;
	int cnt1=0,cnt2=0;
	for(int i=l;i<=mid;i++)add(mat[i].x,mat[i].y,1);
	for(int i=ql;i<=qr;i++){
		int sum=query(q[id[i]].x_2,q[id[i]].y_2)-query(q[id[i]].x_1-1,q[id[i]].y_2)-
			query(q[id[i]].x_2,q[id[i]].y_1-1)+query(q[id[i]].x_1-1,q[id[i]].y_1-1); 
		int num=cur[id[i]]+sum;
		if(num>=q[id[i]].k)q1[++cnt1]=id[i];
		else q2[++cnt2]=id[i],cur[id[i]]=num;
	}
	for(int i=l;i<=mid;i++)add(mat[i].x,mat[i].y,-1);
	int qcnt=ql;
	for(int i=1;i<=cnt1;i++)id[qcnt++]=q1[i];
	for(int i=1;i<=cnt2;i++)id[qcnt++]=q2[i];
	Solve(l,mid,ql,ql+cnt1-1);
	Solve(mid+1,r,ql+cnt1,qr);
}
void solve(){
	//start
	cin.tie(0)->ios::sync_with_stdio(0);
	cout.tie(0);
	cin>>n>>cas;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			int x;
			cin>>x;
			mat[get(i,j)]={i,j,x};
		}
	}
	sort(mat+1,mat+n*n+1);
	for(int i=1;i<=cas;i++){
		cin>>q[i].x_1>>q[i].y_1>>q[i].x_2>>q[i].y_2>>q[i].k;
		id[i]=i;
	}
	Solve(1,n*n,1,cas);
	for(int i=1;i<=cas;i++)cout<<ans[i]<<endl;
}
int main(){
	solve();
	return 0;
}

评论 (0)

还没有评论,来抢沙发吧!
登录 后参与评论