整体二分

OI wiki

个人感觉整体二分就像归并的逆操作。

P3527 [POI2011]MET-Meteors & LOJ2169. 「POI2011 R3 Day2」流星 Meteors

P1527 [国家集训队]矩阵乘法

P3332 [ZJOI2013]K大数查询

自己去看原题吧

树套树 \(\times\),整体二分 \(\sqrt{}\)(离线算法常数吊打树套树)。

我们每次可以定一个阈(yù)值 \(mid\),通过线段树判断每一个询问的答案是否 \(\ge mid\)

  • 若是,则归到第二类。

  • 若否,则将询问的要求 \(-\) 当前 \(\ge mid\) 的数量,然后归到第一类。

将修改按照值也分为两类,同一类中的询问和修改的顺序要保持原样

撤销刚才的线段树操作,递归做即可。

一定要点开来看的代码,不然大概率听不懂
//We'll be counting stars.
#include<bits/stdc++.h>
using namespace std;
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define Rof(i,j,k) for(int i=(j),i##_=(k);i>=i##_;i--)
#define int long long
char buf[1<<21],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read() {
	int x=0,f=1;
	char c=gc();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=gc();}
	return x*f;
}
#define N 50002
struct node{ int op,l,r,c,ans; }q[N];
int n,m,a[N],a1[N],a2[N],t1,t2,t[N<<2],lz[N<<2];
#define mid ((l+r)>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
void tag(int rt,int l,int r,int val){
	t[rt]+=(r-l+1)*val;
	lz[rt]+=val;
}
void pd(int rt,int l,int r){
	if(lz[rt]){
		tag(ls,l,mid,lz[rt]);
		tag(rs,mid+1,r,lz[rt]);
		lz[rt]=0;
	}
}
void add(int rt,int l,int r,int x,int y,int val){
	if(x<=l && r<=y){
		tag(rt,l,r,val);
		return ;
	}
	pd(rt,l,r);
	if(x<=mid) add(ls,l,mid,x,y,val);
	if(y>mid) add(rs,mid+1,r,x,y,val);
	t[rt]=t[ls]+t[rs];
}
int que(int rt,int l,int r,int x,int y){
	if(x<=l && r<=y) return t[rt];
	pd(rt,l,r);
	int res=0;
	if(x<=mid) res+=que(ls,l,mid,x,y);
	if(y>mid) res+=que(rs,mid+1,r,x,y);
	return res;
}
void solve(int l,int r,int L,int R){
	if(L>R || l>r) return ;
	t1=t2=0;
	For(i,L,R){
		if(q[a[i]].op==1){
			if(q[a[i]].c>=mid){
				a2[++t2]=a[i];
				add(1,1,n,q[a[i]].l,q[a[i]].r,1);
			}else{
				a1[++t1]=a[i];
			}
		}else{
			int tmp=que(1,1,n,q[a[i]].l,q[a[i]].r);
			if(tmp>=q[a[i]].c){
				q[a[i]].ans=mid;
				a2[++t2]=a[i];
			}else{
				q[a[i]].c-=tmp;
				a1[++t1]=a[i];
			}
		}
	}
	For(i,L,R){
		if(q[a[i]].op==1){
			if(q[a[i]].c>=mid){
				add(1,1,n,q[a[i]].l,q[a[i]].r,-1);
			}
		}
	}
	For(i,1,t1) a[L+i-1]=a1[i];
	For(i,1,t2) a[L+t1+i-1]=a2[i];
	int gap=L+t1;
	solve(l,mid-1,L,gap-1),solve(mid+1,r,gap,R);
}
signed main(){
	n=read(),m=read();
	For(i,1,m) q[i]=(node){read(),read(),read(),read(),0};
	iota(a+1,a+1+m,1);
	solve(-n,n,1,m);
	For(i,1,m) if(q[i].op==2) printf("%lld\n",q[i].ans);
return 0;}

本题的注意事项:

由于有类似于偏序的关系,我们只能将整体二分写成削弱询问的形式:

void solve(l,r,part){
	decrease the queries to the left part while splitting into two parts by value mid
	revoke the operations have just done
	solve(l,mid-1,leftpart)
	solve(mid+1,r,rightpart)
}

而不能写成撤销的形式:

void solve(l,r,part){
	split into two parts by value mid 
	solve(l,mid-1,leftpart)
	revoke
	solve(mid+1,r,rightpart)
}

(所以说整体二分没有固定的写法,因题而异)

P3834 【模板】可持久化线段树 2

我还是用常数小的 \(O(n\log^2 n)\) 整体二分艹过去了,抱歉(

点击查看代码
//#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
#define mkp make_pair
#define pb emplace_back
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define Rof(i,j,k) for(int i=(j),i##_=(k);i>=i##_;i--)
#define ckmx(a,b) a=max(a,b)
#define ckmn(a,b) a=min(a,b)
#define debug(...) cerr<<"#"<<__LINE__<<": "<<__VA_ARGS__<<endl
#define ll long long
const ll mod=1;
inline ll pw(ll x,ll y){ll r=1;while(y){if(y&1)r=r*x%mod;x=x*x%mod;y>>=1;}return r;}
#define int ll
//global erfen
#define N 200010
#define low (x&(-x))
struct Que{
	int l,r,k;
}q[N];
int b[N],a[N],c[N],n,m,lim,s[N],s0[N],s1[N],t0,t1,ans[N];
vector<int> p[N];
void add(int x,int y){
	while(x<=n){
		c[x]+=y;
		x+=low;
	}
}
int que(int x){
	int res=0;
	while(x){
		res+=c[x];
		x-=low;
	}
	return res;
}
void work(int val,int opt){ for(int i:p[val]) add(i,opt); }
bool calc(Que x){ return que(x.r)-que(x.l-1)>=x.k; }
void solve(int l,int r,int L,int R){
	if(l>r || L>R) return ;
	int mid=(l+r)>>1;
	For(i,l,mid) work(i,1);
	t0=t1=0;
	For(i,L,R){
		if(calc(q[s[i]])){
			ans[s[i]]=mid;
			s0[++t0]=s[i];
		}else{
			s1[++t1]=s[i];
		}
	}
	int tmp=L+t0-1;
	For(i,1,t0) s[L-1+i]=s0[i];
	For(i,1,t1) s[tmp+i]=s1[i];
	solve(mid+1,r,tmp+1,R);
	For(i,l,mid) work(i,-1);
	solve(l,mid-1,L,tmp);
}
signed main(){ios::sync_with_stdio(false),cin.tie(nullptr);
	cin>>n>>m;
	For(i,1,n) cin>>a[i];
	copy(a+1,a+1+n,b+1);
	sort(b+1,b+1+n);
	lim=unique(b+1,b+1+n)-b-1;
	For(i,1,n) a[i]=lower_bound(b+1,b+1+lim,a[i])-b;
	For(i,1,n) p[a[i]].pb(i);
	For(i,1,m) cin>>q[i].l>>q[i].r>>q[i].k;
	iota(s+1,s+1+m,1);
	fill(ans+1,ans+1+m,lim+1);
	solve(1,lim,1,m);
	For(i,1,m) assert(ans[i]<=lim);
	For(i,1,m) cout<<b[ans[i]]<<endl;
return 0;}
posted @ 2022-07-31 15:37  ShaoJia  阅读(37)  评论(0)    收藏  举报