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;
    }
    
  • 相关阅读:
    你所不知道的mfc…mfc项目索引 &mfc调优指南 &mfc vc添加添加子功能指南
    Cu 大彻大悟内存管理 mm (update 0410)
    [转]Linux iostat监测IO状态
    linux virtual memory layout by moniskiller upload [读书笔记]
    河畔找到的 面经笔经
    【转】Linux本地磁盘(硬盘)介绍
    读写UTF8、Unicode文件
    codesmith执行时提示“调用的目标发生了异常”的处理过程经验。
    DB2表信息以及字段信息的表
    iBatis.NET获取resultMap相关数据
  • 原文地址:https://www.cnblogs.com/Zerokei/p/9715997.html
Copyright © 2011-2022 走看看