zoukankan      html  css  js  c++  java
  • 整体二分

    整体二分


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

    例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
    ",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,(cle v)的放左边,其余的放右边。
    • 对于操作2,每次区间查询 ([l,r]) 中满足条件的点的个数S。
    • (Kle 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
    ",Ans[i]);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    消息中间件(一)MQ详解及四大MQ比较
    SIP协议
    PAT (Basic Level) Practice 1008 数组元素循环右移问题
    LeetCode-Algorithms 1. 两数之和
    PAT (Basic Level) Practice 1040 有几个PAT
    PAT (Basic Level) Practice 1023 组个最小数
    PAT (Basic Level) Practice 1021 个位数统计
    PAT (Basic Level) Practice 1007 素数对猜想
    PAT (Basic Level) Practice 1006 换个格式输出整数
    PAT (Basic Level) Practice 1004 成绩排名
  • 原文地址:https://www.cnblogs.com/Zerokei/p/9715997.html
Copyright © 2011-2022 走看看