整体二分

整体二分


当对于一个多询问且可以通过二分答案解决的问题,可以通过整体二分来优化复杂度。

例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;
}

转载于:https://www.cnblogs.com/Zerokei/p/9715997.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值