整体二分学习笔记

用途

在信息学竞赛中,有一部分题可以使用二分的办法来解决。

但是当这种题目有多次询问且每次询问我们对每个查询都直接二分,可能会收获一个 (TLE)

这时候我们就会用到整体二分。

整体二分的主体思路就是把多个查询一起解决。(所以这是一个离线算法)

可以使用整体二分解决的题目需要满足以下性质:

(1)、询问的答案具有可二分性

(2)、修改对判定答案的贡献互相独立,修改之间互不影响效果

(3)、修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值

(4)、贡献满足交换律,结合律,具有可加性

(5)、题目允许使用离线算法

实现

P3332 [ZJOI2013]K大数查询 为例

暴力的做法就是二分一个权值,查看大于等于这个权值的数是不是大于等于 (k) 个。

如果是,就把这个权值增大,否则就去缩小这个权值。

整体二分需要实现一个函数 (solve(al,ar,bl,br))

表示对于当前询问的区间 ([al,ar]),二分的权值在 ([bl,br]) 范围中。

(mids=(bl+br)/2),我们去遍历 ([al,ar]) 中的所有操作。

如果是修改操作并且加入的权值在 ([mids+1,br]) 中,把对应的区间 ([l,r])整体加一。

如果是询问操作,去查询 ([l,r]) 中有多少数,如果有大于等于 (k) 个,我们就把这个询问放到递归的右区间中,否则就放到左区间中。

当二分权值的左右端点相等时就去更新答案。

最后不要忘了把加入的贡献清除。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define rg register
template<typename T>void read(T &x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=2e5+5;
struct trr{
	int l,r,laz,siz;
	long long sum;
}tr[maxn];
void push_up(rg int da){
	tr[da].sum=tr[da<<1].sum+tr[da<<1|1].sum;
}
void push_down(rg int da){
	if(!tr[da].laz) return;
	tr[da<<1].laz+=tr[da].laz;
	tr[da<<1|1].laz+=tr[da].laz;
	tr[da<<1].sum+=1LL*tr[da<<1].siz*tr[da].laz;
	tr[da<<1|1].sum+=1LL*tr[da<<1|1].siz*tr[da].laz;
	tr[da].laz=0;
}
void build(rg int da,rg int l,rg int r){
	tr[da].l=l,tr[da].r=r,tr[da].siz=r-l+1;
	if(l==r) return;
	rg int mids=(l+r)>>1;
	build(da<<1,l,mids),build(da<<1|1,mids+1,r);
}
void xg(rg int da,rg int l,rg int r,rg int val){
	if(tr[da].l>=l && tr[da].r<=r){
		tr[da].laz+=val;
		tr[da].sum+=tr[da].siz*val;
		return;
	}
	push_down(da);
	rg int mids=(tr[da].l+tr[da].r)>>1;
	if(l<=mids) xg(da<<1,l,r,val);
	if(r>mids) xg(da<<1|1,l,r,val);
	push_up(da);
}
long long cx(rg int da,rg int l,rg int r){
	if(tr[da].l>=l && tr[da].r<=r) return tr[da].sum;
	push_down(da);
	rg int mids=(tr[da].l+tr[da].r)>>1;
	rg long long nans=0;
	if(l<=mids) nans+=cx(da<<1,l,r);
	if(r>mids) nans+=cx(da<<1|1,l,r);
	return nans;
}
int n,m,ans[maxn],id[maxn];
struct asd{
	int op,l,r;
	long long val;
	asd(){}
	asd(rg int aa,rg int bb,rg int cc,rg long long dd){
		op=aa,l=bb,r=cc,val=dd;
	}
}b[maxn];
int tmp1[maxn],tmp2[maxn];
void solve(rg int al,rg int ar,rg int bl,rg int br){
	if(bl==br){
		for(rg int i=al;i<=ar;i++) ans[id[i]]=bl;
		return;
	}
	rg int mids=(bl+br)>>1,now1=0,now2=0;
	for(rg int i=al;i<=ar;i++){
		if(b[id[i]].op==1){
			if(b[id[i]].val>mids){
				tmp2[++now2]=id[i];
				xg(1,b[id[i]].l,b[id[i]].r,1);
			} else {
				tmp1[++now1]=id[i];
			}
		} else {
			rg long long tmp=cx(1,b[id[i]].l,b[id[i]].r);
			if(b[id[i]].val<=tmp){
				tmp2[++now2]=id[i];
			} else {
				b[id[i]].val-=tmp;
				tmp1[++now1]=id[i];
			}
		}
	}
	for(rg int i=al;i<=ar;i++){
		if(b[id[i]].op==1){
			if(b[id[i]].val>mids){
				xg(1,b[id[i]].l,b[id[i]].r,-1);
			} 
		}
	}
	for(rg int i=1;i<=now1;i++) id[al+i-1]=tmp1[i];
	for(rg int i=1;i<=now2;i++) id[al+now1+i-1]=tmp2[i];
	solve(al,al+now1-1,bl,mids);
	solve(al+now1,ar,mids+1,br);
}
int main(){
	read(n),read(m);
	rg int aa,bb,cc;
	rg long long dd;
	for(rg int i=1;i<=m;i++){
		read(aa),read(bb),read(cc),read(dd);
		b[i]=asd(aa,bb,cc,dd);
	}
	for(rg int i=1;i<=m;i++) id[i]=i;
	build(1,1,n);
	solve(1,m,-n,n);
	for(rg int i=1;i<=m;i++){
		if(b[i].op==2) printf("%d
",ans[i]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/liuchanglc/p/14526377.html