整体二分专题

何谓整体二分?
一般的二分只适用于单个询问的,如果有很多个询问,就变成了 n 2 n^2 n2或更高
但整体二分则可以迅速处理多个询问的问题
首先需要离线,读入所有询问
然后我们二分答案,这时候我们将询问分成两个部分,如果l==r就直接更新答案,否则考虑分治,询问的答案在左边的丢到左边,答案在右边的则丢到右边,然后两边分别处理
整体二分的时间复杂度则为 O ( ( n + q ) l o g n l o g o p ) O((n+q) logn logop) O((n+q)lognlogop)其中op是solve的过程中的一些辅助操作(如树状数组)

POJ2104区间第k大
静态的区间第k大,可以主席树,也可以整体二分,用树状数组辅助查询
Code:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#define lb(x) (x&-x)
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=500010,inf=1e9;
struct Q{int x,y,k,pos,op;}q[N],q1[N],q2[N];
int n,m,x,y,k,cnt,cnt1,cnt2;
int tr[N],ans[N],a[N];
inline void add(int x,int v){for(;x<=n;x+=lb(x)) tr[x]+=v;}
inline int query(int x) {int res=0;for(;x;x-=lb(x)) res+=tr[x];return res;}
void solve(int l,int r,int L,int R){
	if(l>r || L>R) return;
	if(l==r){
		for(int i=L;i<=R;i++) if(q[i].op) ans[q[i].pos]=l;
		return;
	}
    int mid=(l+r)>>1,cnt1=0,cnt2=0;
	for(int i=L;i<=R;i++){
		if(q[i].op){
			int tmp=query(q[i].y)-query(q[i].x-1);
			if(tmp>=q[i].k) q1[++cnt1]=q[i];
			else q[i].k-=tmp/*这里记得减去tmp,思路和平衡树查找第k大差不多*/,q2[++cnt2]=q[i];
		}
		else{
			if(q[i].x<=mid) q1[++cnt1]=q[i],add(q[i].pos,q[i].y);
			else q2[++cnt2]=q[i];
		}
	}
	for(int i=1;i<=cnt1;i++) if(!q1[i].op) add(q1[i].pos,-q1[i].y);
	for(int i=1;i<=cnt1;i++) q[L+i-1]=q1[i];
	for(int i=1;i<=cnt2;i++) q[L+cnt1+i-1]=q2[i];
	solve(l,mid,L,L+cnt1-1);solve(mid+1,r,L+cnt1,R);
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++) x=read(),q[++cnt]=(Q){x,1,0,i,0};
	for(int i=1;i<=m;i++) x=read(),y=read(),k=read(),q[++cnt]=(Q){x,y,k,i,1};
	solve(-inf,inf,1,cnt);
	for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';
	return 0;
}

HDU动态区间第K大
上面的静态其实是把每个初始数看做一次插入操作,所以启示我们动态的话就可以用删除+插入操作,其余地方相同
如果你不用整体二分,你也可以CDQ,如果你不想分治,那就去写树套树吧我不拦你
Code:

#include<bits/stdc++.h>
#define lb(x) (x&-x)
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=500010,inf=1e9;
struct Q{int x,y,k,pos,op;}q[N],q1[N],q2[N];
int n,m,x,y,k,cnt,cnt1,cnt2;
int tr[N],ans[N],a[N];
inline void add(int x,int v){for(;x<=n;x+=lb(x)) tr[x]+=v;}
inline int query(int x) {int res=0;for(;x;x-=lb(x)) res+=tr[x];return res;}
void solve(int l,int r,int L,int R){
	if(l>r || L>R) return;
	if(l==r){
		for(int i=L;i<=R;i++) if(q[i].op) ans[q[i].pos]=l;
		return;
	}
    int mid=(l+r)>>1,cnt1=0,cnt2=0;
	for(int i=L;i<=R;i++){
		if(q[i].op){
			int tmp=query(q[i].y)-query(q[i].x-1);
			if(tmp>=q[i].k) q1[++cnt1]=q[i];
			else q[i].k-=tmp,q2[++cnt2]=q[i];
		}
		else{
			if(q[i].x<=mid) q1[++cnt1]=q[i],add(q[i].pos,q[i].y);
			else q2[++cnt2]=q[i];
		}
	}
	for(int i=1;i<=cnt1;i++) if(!q1[i].op) add(q1[i].pos,-q1[i].y);
	for(int i=1;i<=cnt1;i++) q[L+i-1]=q1[i];
	for(int i=1;i<=cnt2;i++) q[L+cnt1+i-1]=q2[i];
	solve(l,mid,L,L+cnt1-1);solve(mid+1,r,L+cnt1,R);
}
int t[N];
int main(){
	n=read();int ki;
	for(int i=1;i<=n;i++) x=read(),a[i]=x,q[++cnt]=(Q){x,1,0,i,0};
	m=read();
	for(int i=1;i<=m;i++){
		ki=read();
		if(ki==1) {x=read(),y=read();q[++cnt]=(Q){a[x],-1,0,x,0};q[++cnt]=(Q){y,1,0,x,0};a[x]=y;}/*分为插入和删除*/
		else t[i]=1,x=read(),y=read(),k=read(),q[++cnt]=(Q){x,y,k,i,1};
	}
	solve(-inf,inf,1,cnt);
	for(int i=1;i<=m;i++) if(t[i]) cout<<ans[i]<<'\n';
	return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值