用途
在信息学竞赛中,有一部分题可以使用二分的办法来解决。
但是当这种题目有多次询问且每次询问我们对每个查询都直接二分,可能会收获一个 (TLE)。
这时候我们就会用到整体二分。
整体二分的主体思路就是把多个查询一起解决。(所以这是一个离线算法)
可以使用整体二分解决的题目需要满足以下性质:
(1)、询问的答案具有可二分性
(2)、修改对判定答案的贡献互相独立,修改之间互不影响效果
(3)、修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值
(4)、贡献满足交换律,结合律,具有可加性
(5)、题目允许使用离线算法
实现
暴力的做法就是二分一个权值,查看大于等于这个权值的数是不是大于等于 (k) 个。
如果是,就把这个权值增大,否则就去缩小这个权值。
整体二分需要实现一个函数 (solve(al,ar,bl,br)),
表示对于当前询问的区间 ([al,ar]),二分的权值在 ([bl,br]) 范围中。
令 (mids=(bl+br)/2),我们去遍历 ([al,ar]) 中的所有操作。
如果是修改操作并且加入的权值在 ([mids+1,br]) 中,把对应的区间 ([l,r])整体加一。
如果是询问操作,去查询 ([l,r]) 中有多少数,如果有大于等于 (k) 个,我们就把这个询问放到递归的右区间中,否则就放到左区间中。
当二分权值的左右端点相等时就去更新答案。
最后不要忘了把加入的贡献清除。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define rg register
template<typename T>void read(T &x){
x=0;rg int fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
x*=fh;
}
const int maxn=2e5+5;
struct trr{
int l,r,laz,siz;
long long sum;
}tr[maxn];
void push_up(rg int da){
tr[da].sum=tr[da<<1].sum+tr[da<<1|1].sum;
}
void push_down(rg int da){
if(!tr[da].laz) return;
tr[da<<1].laz+=tr[da].laz;
tr[da<<1|1].laz+=tr[da].laz;
tr[da<<1].sum+=1LL*tr[da<<1].siz*tr[da].laz;
tr[da<<1|1].sum+=1LL*tr[da<<1|1].siz*tr[da].laz;
tr[da].laz=0;
}
void build(rg int da,rg int l,rg int r){
tr[da].l=l,tr[da].r=r,tr[da].siz=r-l+1;
if(l==r) return;
rg int mids=(l+r)>>1;
build(da<<1,l,mids),build(da<<1|1,mids+1,r);
}
void xg(rg int da,rg int l,rg int r,rg int val){
if(tr[da].l>=l && tr[da].r<=r){
tr[da].laz+=val;
tr[da].sum+=tr[da].siz*val;
return;
}
push_down(da);
rg int mids=(tr[da].l+tr[da].r)>>1;
if(l<=mids) xg(da<<1,l,r,val);
if(r>mids) xg(da<<1|1,l,r,val);
push_up(da);
}
long long cx(rg int da,rg int l,rg int r){
if(tr[da].l>=l && tr[da].r<=r) return tr[da].sum;
push_down(da);
rg int mids=(tr[da].l+tr[da].r)>>1;
rg long long nans=0;
if(l<=mids) nans+=cx(da<<1,l,r);
if(r>mids) nans+=cx(da<<1|1,l,r);
return nans;
}
int n,m,ans[maxn],id[maxn];
struct asd{
int op,l,r;
long long val;
asd(){}
asd(rg int aa,rg int bb,rg int cc,rg long long dd){
op=aa,l=bb,r=cc,val=dd;
}
}b[maxn];
int tmp1[maxn],tmp2[maxn];
void solve(rg int al,rg int ar,rg int bl,rg int br){
if(bl==br){
for(rg int i=al;i<=ar;i++) ans[id[i]]=bl;
return;
}
rg int mids=(bl+br)>>1,now1=0,now2=0;
for(rg int i=al;i<=ar;i++){
if(b[id[i]].op==1){
if(b[id[i]].val>mids){
tmp2[++now2]=id[i];
xg(1,b[id[i]].l,b[id[i]].r,1);
} else {
tmp1[++now1]=id[i];
}
} else {
rg long long tmp=cx(1,b[id[i]].l,b[id[i]].r);
if(b[id[i]].val<=tmp){
tmp2[++now2]=id[i];
} else {
b[id[i]].val-=tmp;
tmp1[++now1]=id[i];
}
}
}
for(rg int i=al;i<=ar;i++){
if(b[id[i]].op==1){
if(b[id[i]].val>mids){
xg(1,b[id[i]].l,b[id[i]].r,-1);
}
}
}
for(rg int i=1;i<=now1;i++) id[al+i-1]=tmp1[i];
for(rg int i=1;i<=now2;i++) id[al+now1+i-1]=tmp2[i];
solve(al,al+now1-1,bl,mids);
solve(al+now1,ar,mids+1,br);
}
int main(){
read(n),read(m);
rg int aa,bb,cc;
rg long long dd;
for(rg int i=1;i<=m;i++){
read(aa),read(bb),read(cc),read(dd);
b[i]=asd(aa,bb,cc,dd);
}
for(rg int i=1;i<=m;i++) id[i]=i;
build(1,1,n);
solve(1,m,-n,n);
for(rg int i=1;i<=m;i++){
if(b[i].op==2) printf("%d
",ans[i]);
}
return 0;
}