整体二分学习笔记
前情提要:现在的整体二分已经不独属于 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;
}
当然如果只用整体二分的例题还是不难的,但是要离散化。

浙公网安备 33010602011771号