整体二分学习笔记

引入

对于全局区间第 \(k\) 小,我们可以用的方法是二分;但当询问从全局变为区间、单次变多次时,我们再用这样的方法就会 TLE。所以我们需要进行优化,也就引出了整体二分。

介绍

整体二分,作为一种离线算法,常用来解决一些答案具有二分性的问题。

我们先思考二分的操作(以查询第 \(k\) 小为例):

记当前值域为 \([l,r]\),中点为 \(mid\),我们统计值域区间比 \(mid\) 小的数有多少,设有 \(x\) 个数。
\(x>=k\),说明最终的答案在左区间,我们将 \(r\) 缩小范围;反之,我们将 \(k\) 减去 \(x\) ,将 \(l\) 增大范围。

面对多次询问时,这种方法就显得有些低效,于是我们考虑也对询问进行二分。

具体的,我们将每个数看成一次添加操作,将询问离线下来。依然是对值域进行二分,对于每个添加操作,如果添加的数比 \(mid\) 小,我们就添加这个数;对于每个询问,我们统计比 \(mid\) 小的数有多少,然后对于所有操作,我们将其分为两类:需要向左区间值域递归的询问和需要向右区间值域递归的询问。然后我们对询问分治,将操作和值域一起向下递归。

总结一下整体二分大致思路:将操作离线->对值域二分->将操作按递归区间分类->递归处理。

例题

静态区间第 k 小

具体思路前面已经提到了,复杂度 \(O(nlog^2n)\)

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
static char pbuf[1000000],*p1=pbuf,*p2=pbuf;
#define getchar() p1==p2&&(p2=(p1=pbuf)+fread(pbuf,1,1000000,stdin),p1==p2)?EOF:*p1++
inline int read() {
	int s=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s;
}
struct Qry{
	int l,r,k,id;
}q[N];
int li[N],len,n,m,ans[N];
queue<Qry>L,R;
struct BIT{
	int c[N];
	inline void add(int p,int x) {while(p<=n) c[p]+=x,p+=p&-p;}
	inline int qry(int p,int res=0) {while(p) res+=c[p],p-=p&-p;return res;}
}t;
inline void solve(int l,int r,int x,int y) {
	if(x>y) return ;
	if(l==r) {
		for(int i=x;i<=y;++i)
			if(q[i].id) ans[q[i].id]=li[l];
		return ;
	}
	int mid=(l+r)>>1;
	for(int i=x;i<=y;++i) {
		if(q[i].id) {
			int k=t.qry(q[i].r)-t.qry(q[i].l-1);
			if(k>=q[i].k) L.push(q[i]);
			else q[i].k-=k,R.push(q[i]);
		}
		else {
			if(q[i].k<=mid) t.add(q[i].l,1),L.push(q[i]);
			else R.push(q[i]);
		}
	}
	for(int i=x;i<=y;++i)
		if(!q[i].id&&q[i].k<=mid) t.add(q[i].l,-1);
	int siz=L.size(),cnt=x-1;
	while(!L.empty()) q[++cnt]=L.front(),L.pop();
	while(!R.empty()) q[++cnt]=R.front(),R.pop();
	solve(l,mid,x,x+siz-1);solve(mid+1,r,x+siz,y);
}
signed main() {
	n=read(),m=read();
	for(int i=1;i<=n;++i) li[i]=q[i].k=read(),q[i].l=i;
	sort(li+1,li+n+1);len=unique(li+1,li+n+1)-li-1;
	for(int i=1;i<=n;++i) q[i].k=lower_bound(li+1,li+len+1,q[i].k)-li;
	for(int i=n+1;i<=n+m;++i) q[i].l=read(),q[i].r=read(),q[i].k=read(),q[i].id=i-n;
	solve(1,len,1,n+m);
	for(int i=1;i<=m;++i) cout<<ans[i]<<'\n';
	return 0;
}

P3527 MET-Meteors

将每个国家看成一个询问,对时间进行二分,具体操作与前面一样,复杂度仍是 \(O(nlog^2n)\)

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
typedef unsigned long long ull;
static char pbuf[1000000],*p1=pbuf,*p2=pbuf;
#define getchar() p1==p2&&(p2=(p1=pbuf)+fread(pbuf,1,1000000,stdin),p1==p2)?EOF:*p1++
int S,i;char ch;
inline int read() {
	S=0;ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') S=(S<<3)+(S<<1)+(ch^48),ch=getchar();
	return S;
}
struct BIT{
	ull tr[N];
	inline void add(int p,int x) {while(p<N) tr[p]+=x,p+=p&-p;}
	inline void Add(int l,int r,int x) {add(l,x);add(r+1,-x);}
	inline ull qry(int p,ull res=0) {while(p) res+=tr[p],p-=p&-p;return res;}
}t;
struct Opt{int l,r,k,id;}q[N<<1];
vector<int>c[N];
int k,tot,ans[N],n,m;
Opt R[N];
#define mid ((l+r)>>1)
inline void solve(int l,int r,int x,int y) {
	if(x>y) return ;
	int cnt=x-1,top=0;
	if(l==r) {
		for(i=x;i<=y;++i)
			if(!q[i].l) ans[q[i].id]=l;
		return ;
	}
	for(i=x;i<=y;++i) {
		if(!q[i].l) {
			ull w=0;
			for(auto v:c[q[i].id]) w+=t.qry(v);
			if(w>=(ull)q[i].k) q[++cnt]=q[i];
			else q[i].k-=w,R[++top]=q[i];
		}
		else {
			if(q[i].id<=mid) t.Add(q[i].l,q[i].r,q[i].k),q[++cnt]=q[i];
			else R[++top]=q[i];
		}
	}
	for(i=x;i<=y;++i)
		if(q[i].l&&q[i].id<=mid) t.Add(q[i].l,q[i].r,-q[i].k);
	int siz=cnt;for(int i=1;i<=top;++i) q[++cnt]=R[i];
	solve(l,mid,x,siz);solve(mid+1,r,siz+1,y);
}
signed main() {
	n=read(),m=read();
	for(i=1;i<=m;++i) c[read()].push_back(i);
	for(i=1;i<=n;++i) t.tr[i]=read();
	k=read();int l,r,x;
	for(i=1;i<=k;++i) {
		l=read(),r=read(),x=read();
		if(l<=r) q[++tot]={l,r,x,i};
		else q[++tot]={l,m,x,i},q[++tot]={1,r,x,i};
	}
	for(i=1;i<=n;++i) q[++tot]={0,0,(int)t.tr[i],i},t.tr[i]=0;
	solve(1,k+1,1,tot);
	for(i=1;i<=n;++i) {
		if(ans[i]==k+1||ans[i]==0) puts("NIE");
		else printf("%d\n",ans[i]);
	}	
	return 0;
}

感谢 @Zelensky 对本文的指导。

posted @ 2025-09-30 17:04  leizepromax  阅读(37)  评论(1)    收藏  举报