整体二分
当对于一个多询问且可以通过二分答案解决的问题,可以通过整体二分来优化复杂度。
例1 区间第K值
Description
多询问,每次询问区间的第K大值
Solution
- 二分权值V,把权值\(\le\)V的点加入BIT中,然后询问第K大值\(\le\)V的放左边,>V的放右边
- 右边的K要减去区间中权值\(\le\)V的点的个数
- 然后分治下去即可
- 复杂度\(O(qlog^2n)\)
#include<cstdio>
#include<algorithm>
using namespace std;
#define FOR(i,x,y) for(int i=(x),i##_END=(y);i<=i##_END;++i)
#define M 30005
int A[M],B[M],Id[M],Ans[M];
struct GG{
int x,y,k,id;
}Q[M],Q1[M],Q2[M];
bool cmp(int x,int y){
return A[x]<A[y];
}
struct BIT{
int Sum[M];
void add(int x,int a){while(x<M){Sum[x]+=a;x+=x&-x;}}
int query(int x){int res=0;while(x){res+=Sum[x];x-=x&-x;}return res;}
}BIT;
void Solve(int l,int r,int Ql,int Qr,int L,int R){
if(Ql>Qr||l>r)return;
if(L==R){
FOR(i,Ql,Qr)Ans[Q[i].id]=L;
return;
}
int mid=(L+R)>>1,las=r;
FOR(i,l,r){
if(A[Id[i]]<=mid){
BIT.add(Id[i],1);
}else {las=i-1;break;}
}
int c1=Ql-1,c2=Qr;
FOR(i,Ql,Qr){
int s=BIT.query(Q[i].y)-BIT.query(Q[i].x-1);
if(s>=Q[i].k)Q1[++c1]=Q[i];
else Q[i].k-=s,Q2[c2--]=Q[i];
}
FOR(i,Ql,c1)Q[i]=Q1[i];
FOR(i,c2+1,Qr)Q[i]=Q2[i];
FOR(i,l,las)BIT.add(Id[i],-1);
Solve(l,las,Ql,c1,L,mid);
Solve(las+1,r,c2+1,Qr,mid+1,R);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
FOR(i,1,n)scanf("%d",&A[i]),B[i]=A[i],Id[i]=i;
FOR(i,1,m){
scanf("%d%d%d",&Q[i].x,&Q[i].y,&Q[i].k);
Q[i].k=Q[i].y-Q[i].x+2-Q[i].k,Q[i].id=i;
}
sort(B+1,B+n+1);
int c=unique(B+1,B+n+1)-B-1;
FOR(i,1,n)A[i]=lower_bound(B+1,B+c+1,A[i])-B;
sort(Id+1,Id+n+1,cmp);
Solve(1,n,1,m,1,c);
FOR(i,1,m)printf("%d\n",B[Ans[i]]);
return 0;
}
例2 K大数查询
Description
有\(n\)个位置和\(m\)个操作。操作有两种:
如果操作形如1 a b c,表示往第\(a\)个位置到第\(b\)个位置每个位置加入一个数\(c\)。
如果操作形如 2 a b c,表示询问从第\(a\)个位置到第\(b\)个位置,第\(c\)大的数是多少?
Solution
- 二分权值V,根据时间排序(顺序)
- 然后对于操作1,\(c\le v\)的放左边,其余的放右边。
- 对于操作2,每次区间查询 \([l,r]\) 中满足条件的点的个数S。
- \(K\le S\)的询问放左边,其余的放右边,同样右边的询问要减去当前的贡献。
例3 HDU5412 CRB and Queries
Description
有\(n\)个位置和\(m\)个操作。操作有两种:
如果操作形如1 a b ,表示把第\(a\)个的数变成\(b\)。
如果操作形如 2 a b c,表示询问从第\(a\)个位置到第\(b\)个位置,第\(c\)小的数是多少?
- 二分权值,根据时间顺序排序
- 通过拆点把操作转化成在某个位置(x)加入/删除一个权值为v的点
- 然后和例2的操作相同,利用BIT单点更新区间查询
#include<cstdio>
#include<algorithm>
using namespace std;
#define FOR(i,x,y) for(int i=(x),i##_END=(y);i<=i##_END;++i)
#define DOR(i,x,y) for(int i=(x),i##_END=(y);i>=i##_END;--i)
const int N=100005;
struct node{
int op,l,r,v;
}Q[N*3],Q1[N*3];
int A[N],B[N<<1],Ans[N];
int Sum[N];
void update(int x,int v){
while(x<N){Sum[x]+=v;x+=x&-x;}
}
int query(int x){
int res=0;
while(x){res+=Sum[x];x-=x&-x;}
return res;
}
void Solve(int l,int r,int L,int R){
if(l>r)return;
if(L==R){
FOR(i,l,r)if(Q[i].op)Ans[Q[i].op]=B[L];
return;
}
int mid=(L+R)>>1;
int t1=l-1,t2=r;
FOR(i,l,r){
if(!Q[i].op){
if(Q[i].v<=mid){
update(Q[i].l,Q[i].r);
Q1[++t1]=Q[i];
}else Q1[t2--]=Q[i];
}else{
int s=query(Q[i].r)-query(Q[i].l-1);
if(Q[i].v<=s)Q1[++t1]=Q[i];
else {
Q[i].v-=s;
Q1[t2--]=Q[i];
}
}
}
if(t2<=r)reverse(Q1+t2+1,Q1+r+1);
FOR(i,l,r)Q[i]=Q1[i];
FOR(i,l,t1)if(!Q[i].op)update(Q[i].l,-Q[i].r);
Solve(l,t1,L,mid);
Solve(t2+1,r,mid+1,R);
}
int main(){
int n,q;
while(~scanf("%d",&n)){
int m=0,c=0;
FOR(i,1,n){
int x;
scanf("%d",&x);
Q[++m]=(node){0,i,1,x};
B[++c]=x,A[i]=x;
}
scanf("%d",&q);
FOR(i,1,q){
int op,l,r,v;
scanf("%d",&op);
if(op==1){
scanf("%d%d",&l,&v);
Q[++m]=(node){0,l,-1,A[l]};
A[l]=v;
Q[++m]=(node){0,l,1,A[l]};
B[++c]=v;
}
else {
scanf("%d%d%d",&l,&r,&v);
Q[++m]=(node){i,l,r,v};
}
}
sort(B+1,B+c+1);
c=unique(B+1,B+c+1)-B-1;
FOR(i,1,m)if(Q[i].op==0){
Q[i].v=lower_bound(B+1,B+c+1,Q[i].v)-B;
}
FOR(i,1,q)Ans[i]=-1;
Solve(1,m,1,c);
FOR(i,1,q)if(Ans[i]!=-1)printf("%d\n",Ans[i]);
}
return 0;
}