整体二分学习笔记

前情提要:现在的整体二分已经不独属于 cyt 了!!!

整体二分跟分治很像(好吧就是用分治实现的),而且符合二分的思路。

这个分治需要记 \(l,r,ql,qr\) 分别是二分的上下界和询问的区间。

把在 \([l,mid]\) 的询问拎出来,\([mid+1,r]\) 的询问拎出来,继续二分即可。

当然在这期间需要对期间的询问进行处理。

这样讲还是很懵的,还是以一道例题讲解。

[P3332 ZJOI2013] K大数查询 - 洛谷 (luogu.com.cn)

可以先想一下如果直接二分怎么做,就是直接二分这个第 \(k\) 大的值,查找小于这个数的数有多少个,比价即可。

那我们整体二分也是不难的。

\(l=r\) 就直接更新答案。

反之,直接二分在 \([ql,qr]\) 区间的数。可以计算在 \([mid+1,r]\) 之中的数对答案的影响(因为是 K 大数),这里用增加操作,可以用线段树区间加。查询的时候跟普通的二分一样,但是这里不是直接改变 \(l,r\) 而是放进一个临时数组里,到时候再塞回去即可,这个整体二分的时间复杂度是 \(O(n\log^2 n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int  N=5E4+5;
int n,m,ans[N],c[N];
int flag[N];
struct SEG{
	#define ls p<<1
	#define rs p<<1|1
	int c[N<<2],tag[N<<2],rec[N<<2];
	void pushup(int p){
		c[p]=c[ls]+c[rs];
	}
	void pushdown(int p,int l,int r){
		if(rec[p]){
			tag[ls]=0,tag[rs]=0;
			c[ls]=0,c[rs]=0;
			rec[ls]=1,rec[rs]=1;
			rec[p]=0;
		}
		if(tag[p]){
			int mid=(l+r>>1);
			tag[ls]+=tag[p];
			tag[rs]+=tag[p];
			c[ls]+=tag[p]*(mid-l+1);
			c[rs]+=tag[p]*(r-mid);
			tag[p]=0;
		}
	}
	void change(int p,int l,int r,int L,int R,int x){
		if(L<=l&&r<=R) return tag[p]+=x,c[p]+=x*(r-l+1),void();
		int mid=l+r>>1;
		pushdown(p,l,r);
		if(L<=mid)change(ls,l,mid,L,R,x);
		if(R> mid)change(rs,mid+1,r,L,R,x);
		pushup(p); 
	}
	int query(int p,int l,int r,int L,int R){
		if(L<=l&&r<=R)return c[p];
		int mid=l+r>>1,res=0;
		pushdown(p,l,r);
		if(L<=mid)res+=query(ls,l,mid,L,R);
		if(R> mid)res+=query(rs,mid+1,r,L,R);
		return res;
	}
	void clear(){
		rec[1]=1,tag[1]=c[1]=0;
	}
}seg;
struct node{
	int l,r,op,c,id;
}que[N],tr[N],tl[N];
void solve(int l,int r,int ql,int qr){
	if(l==r){
		for(int i=ql;i<=qr;i++)
			if(que[i].op==2)ans[que[i].id]=l;	
		return ;
	}
	int mid=l+r>>1;
	int fl=0,fr=0,L=0,R=0;
	seg.clear();
	for(int i=ql;i<=qr;i++){
		if(que[i].op ==1){
			if(que[i].c > mid){
				seg.change(1,1,n,que[i].l ,que[i].r,1); 
				tr[++R]=que[i];
			} 
			else tl[++L]=que[i];	
		}//添加操作 
		else {
			int tmp=seg.query(1,1,n,que[i].l ,que[i].r); 
			if(que[i].c > tmp){
				fl=1;
				que[i].c -= tmp;
				tl[++L]=que[i];
			}//这个数比 mid 大  
			else fr=1,tr[++R]=que[i];	
		}//查询 
	}
	for(int i=ql;i<=ql+L-1;i++)que[i]=tl[i-ql+1];
	for(int i=ql+L;i<=qr;i++) que[i]=tr[i-(ql+L)+1];
	if(fl)solve(l,mid,ql,ql+L-1);
	if(fr)solve(mid+1,r,ql+L,qr);
}//答案在 [l,r] ,查询在 [ql,qr] 
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1,l,r,op,k;i<=m;i++){
		scanf("%lld%lld%lld%lld",&op,&l,&r,&k);
		que[i]={l,r,op,k,i};
		c[i]=k;
		if(que[i].op ==2 )flag[que[i].id ]=1;
	}
	sort(c+1,c+m+1);
	int cnt=unique(c+1,c+m+1)-c-1;
	for(int i=1;i<=m;i++)
		if(que[i].op == 1 )que[i].c=lower_bound(c+1,c+cnt+1,que[i].c)-c;
	solve(1,cnt,1,m);
	for(int i=1;i<=m;i++)
		if(flag[i])cout<<c[ans[i]]<<endl;
	return 0;
}

当然如果只用整体二分的例题还是不难的,但是要离散化。

P2617 Dynamic Rankings - 洛谷 (luogu.com.cn)

[P7424 THUPC 2017] 天天爱射击 - 洛谷 (luogu.com.cn)

posted @ 2025-05-18 22:21  hnczy  阅读(23)  评论(0)    收藏  举报