二分答案

今天来整理一下最近做的一类题目,其基本思想就是二分答案,我们来说几道例题

\(No.1\;\; AGC006D\;Median\;Pyramid\;Hard\)

link

这个题我们发现如果我们按题意模拟,我们发现我们无论如何都想不出如何在不改变原序列的情况下,使得能在正确的复杂度内得到答案,那么我们就要想优化。

我们考虑二分答案,设当前所枚举的答案为 \(mid\),最后顶上的答案为 \(res\),那么我们就将所有大于等于 \(mid\) 的数的值置为 \(1\),将小于 \(mid\) 的数置为 \(0\),并且我们发现只要有相同的两个数字相邻,那么它上面的数字还是他,那么我们就可以在 \(O(n)\) 的复杂度内递推了。

我们还需要特判没有出现相邻两个数相同的情况,那么此时的答案即为我们两边的数字。

如果 \(res=1\) 那么说明最终答案大于等于 \(mid\),我们令 \(l=mid+1\),并记录此时的最优答案,反之,令 \(r=mid-1\)

\(code\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];
int n,T;
bool check(int x){
    for(int i=0;i<n-1;i++){
        if((a[n-i]<x&&a[n-i-1]<x)||(a[n+i]<x&&a[n+i+1]<x))return 0;
        if((a[n-i]>=x&&a[n-i-1]>=x)||(a[n+i]>=x&&a[n+i+1]>=x))return 1;
    }
    return a[1]>=x;
}
void work(){
    scanf("%d",&n);
    for(int i=1;i<=2*n-1;i++)scanf("%d",&a[i]);
    int l=1,r=2*n-1,res=0;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid))res=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d\n",res);
}
int main(){work();return 0;}

\(No.2\;\; CF484E\;Sign\;on\;Fence\)

link

这是一道数据结构题,当然也用到了这种思路。我们发现对于一个区间中的长度为 \(k\) 的子区间的最小值的最大值,我们考虑进行二分。

我们设当前所枚举的答案为 \(mid\),那么我们将大于等于 \(mid\) 的数视作 \(1\),小于 \(mid\) 的数视作 \(0\),我们的目的就是找出这个区间中是否有一个长度大于等于 \(k\) 的连续的 \(1\),如果有,那么答案就大于等于 \(mid\),我们令 \(l=mid+1\),并记录最优答案;反之,我们令 \(r=mid-1\)。这个是可以用线段树维护的。

当然,因为我们的每一个 \(mid\) 所对应的线段树不相同,所以我们考虑使用主席树来做。

\(code\)

#include<bits/stdc++.h>
#define lson(rt) (tree[rt].ls)
#define rson(rt) (tree[rt].rs)
using namespace std;
const int N=2e5+10;
int n,q,a[N],id[N],lsh[N],cnt,len,root[N];
bool cmp(int x,int y){return a[x]<a[y];}
struct President_Tree{
    struct Seg{
        int ls,rs,pre,suf,len,length;
        Seg(){ls=0,rs=0,pre=0,suf=0,len=0,length=0;}
    }tree[N*50];
    int tot;
    inline friend Seg operator + (Seg a,Seg b){
        Seg c;
        c.length=a.length+b.length;
        c.len=max(a.len,max(b.len,a.suf+b.pre));
        c.pre=((a.pre==a.length)?a.len+b.pre:a.pre);
        c.suf=((b.suf==b.length)?a.suf+b.len:b.suf);
        return c;    
    }
    void pushup(int rt){
        int lls=lson(rt),rrs=rson(rt);
        tree[rt]=tree[lson(rt)]+tree[rson(rt)];
        lson(rt)=lls,rson(rt)=rrs;
    }
    int build(int rt,int l,int r){
        int k=++tot;
        tree[k]=tree[rt];
        if(l==r){
            tree[k].pre=tree[k].suf=tree[k].len=0;
            tree[k].length=1;
            return k;
        }
        int mid=(l+r)>>1;
        lson(k)=build(lson(rt),l,mid);
        rson(k)=build(rson(rt),mid+1,r);
        pushup(k);
        return k;
    }
    int update(int rt,int l,int r,int pos,int val){
        int k=++tot;
        tree[k]=tree[rt];
        if(l==r){
            tree[k].pre=tree[k].suf=tree[k].len=val;
            tree[k].length=1;
            return k;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)lson(k)=update(lson(rt),l,mid,pos,val);
        else rson(k)=update(rson(rt),mid+1,r,pos,val);
        pushup(k);
        return k;
    }
    Seg ask(int rt,int l,int r,int L,int R){
        if(l<=L&&R<=r)return tree[rt];
        int mid=(L+R)>>1;
        Seg res1,res2;
        if(l<=mid)res1=ask(lson(rt),l,r,L,mid);
        if(r>mid)res2=ask(rson(rt),l,r,mid+1,R);
        return res1+res2;
    }
    bool check(int l,int r,int k,int val){
        Seg tmp;
        tmp=ask(root[val],l,r,1,n);
        return tmp.len>=k;
    }
    int getans(int l,int r,int k){
        int L=1,R=len,ans=0;
        while(L<=R){
            int mid=(L+R)>>1;
            if(check(l,r,k,mid))L=mid+1,ans=mid;
            else R=mid-1;
        }
        return lsh[ans];
    }
}T;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),lsh[++cnt]=a[i],id[i]=i;
    sort(id+1,id+1+n,cmp);
    sort(lsh+1,lsh+1+cnt);
    len=unique(lsh+1,lsh+1+cnt)-lsh-1;
    int r=n+1;
    root[len+1]=T.build(root[len+1],1,n);
    for(int i=len;i>=1;i--){
        root[i]=root[i+1];
        while(r-1>=1&&a[id[r-1]]==lsh[i]){
            r--;
            root[i]=T.update(root[i],1,n,id[r],1);
        }
    }
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        int s1=0,s2=0,s3=0;
        scanf("%d%d%d",&s1,&s2,&s3);
        printf("%d\n",T.getans(s1,s2,s3));
    }
    return 0;
}

\(No.3\;\; HEOI2016\; 排序\)

link

这个题让人一看就想打暴力,并且因为我们发现当我们按他所说的操作进行时,最坏复杂度还是为 \(O(mg(n))\) 的(其中 \(g(n)\) 为你所用的排序方式,好像最好可以做到 \(O(n)\)),那么我们就要想优化。

我们考虑二分答案,我们设当前所枚举的答案为 \(mid\),那么我们将大于等于 \(mid\) 的数视作 \(1\),将小于 \(mid\) 的视作 \(0\),我们就发现排序变成了 \(0,1\) 变换。

举个例子,我们假设当前要排序的区间为 \([l,r]\),设当前 \([l,r]\)\(1\) 的数量为 \(cnt\),那么假如我们降序排序,就是将 \([l,l+cnt-1]\) 区间内的数置为 \(1\),将 \([l+cnt,r]\) 区间内的数置为 \(0\) 即可;同理,升序也是一样的。我们发现这种转化是对的,我们最终查 \(q\) 这个位置,如果此时这个位置为 \(1\),那么我们最终的答案就大于等于 \(mid\),我们就令 \(l=mid+1\),并更新最优答案,反之,令 \(r=mid-1\)

那么我们就可以通过二分答案和线段树在 \(O(nlog^2n)\) 的复杂度内解决这道题了。

当然,如果 \(cnt=0\)\(cnt=n\) 时可能会出现我们更改的区间的 \(l>r\) 的情况,我们判断一下就可以了。

\(code\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,a[N],b[N],q;
struct Ask{int opt,l,r;}ask[N];
struct Segment_Tree{
	#define lson (rt << 1)
	#define rson (rt << 1 | 1)
	struct Seg{int sum,lazy;}tree[N<<2];
	void pushup(int rt){tree[rt].sum=tree[lson].sum+tree[rson].sum;}
	void add(int rt,int val,int l,int r){tree[rt].sum=(r-l+1)*val,tree[rt].lazy=val;}
	void pushdown(int rt,int l,int r){if(tree[rt].lazy!=-1){int mid=(l+r)>>1;add(lson,tree[rt].lazy,l,mid),add(rson,tree[rt].lazy,mid+1,r);tree[rt].lazy=-1;}}
	void build(int rt,int l,int r){
		tree[rt].lazy=-1;
		if(l==r){tree[rt].sum=a[l];return ;}
		int mid=(l+r)>>1;
		build(lson,l,mid);
		build(rson,mid+1,r);
		pushup(rt);
	}
	void update(int rt,int l,int r,int L,int R,int val){
		if(l>r)return ;
		if(l<=L&&R<=r){add(rt,val,L,R);return ;}
		int mid=(L+R)>>1;
		pushdown(rt,L,R);
		if(l<=mid)update(lson,l,r,L,mid,val);
		if(r>mid)update(rson,l,r,mid+1,R,val);
		pushup(rt);
	}
	int ask(int rt,int l,int r,int L,int R){
		if(l<=L&&R<=r)return tree[rt].sum;
		int mid=(L+R)>>1;
		pushdown(rt,L,R);
		int ans=0;
		if(l<=mid)ans+=ask(lson,l,r,L,mid);
		if(r>mid)ans+=ask(rson,l,r,mid+1,R);
		return ans;
	}
}T;
bool check(int val){
	for(int i=1;i<=n;i++){
		if(b[i]<val)a[i]=0;
		else a[i]=1;
	}
	T.build(1,1,n);
	for(int i=1;i<=m;i++){
		int O=ask[i].opt,L=ask[i].l,R=ask[i].r;
		int cnt=T.ask(1,L,R,1,n);
		if(O==1){
			T.update(1,L,L+cnt-1,1,n,1);
			T.update(1,L+cnt,R,1,n,0);
		}
		else {
			T.update(1,R-cnt+1,R,1,n,1);
			T.update(1,L,R-cnt,1,n,0);
		}
	}
	if(T.ask(1,q,q,1,n))return true;
	return false;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
	for(int i=1;i<=m;i++)scanf("%d%d%d",&ask[i].opt,&ask[i].l,&ask[i].r);
	scanf("%d",&q);
	int l=1,r=n,res=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid))l=mid+1,res=mid;
		else r=mid-1;
	}
	printf("%d\n",res);
	return 0;
}

\(updating...\)

posted @ 2022-11-06 17:39  hxqasd  阅读(49)  评论(1编辑  收藏  举报