何谓整体二分?
一般的二分只适用于单个询问的,如果有很多个询问,就变成了
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;
}