• Codeforces 460C 二分结果+线段树维护


    发现最近碰到好多次二分结果的题目,上次多校也是,被我很机智的快速过了,这个思想确实非常不错。在正面求比较难处理的时候,二分结果再判断是否有效往往柳暗花明。

    这个题目给定n个数字的序列,可以操作m次,每次要操作w个连续的数字,每次的操作将使得该段连续数字的数都+1,最后求整个序列最小值的最大值

    求最小值最大,明显的二分结果的题目,我一开始还是在ACdream那个群里看到这个题,说是二分+线段树的题目,我就来做了一下。。首先二分部分很容易,下界就是初始序列的最小值,上界就是 下界+m,至于怎么判断这个就要想一下,我一开始还觉得不用线段树,因为我每次枚举完结果,对整个序列扫一遍,对不符合要求的数+到正好符号要求不就行了。

    不过这个里面因为是要+连续一段,你普通的找到一个不符合条件的数,然后对他以及他之后的w-1个数都进行一次加法,很耗时啊,所以这个就是为什么要用线段树,其实我觉得用扫描线估计也可以,对某个起始点设置+1,起始点+w-1设置为-1,然后用个东西维护,++--的,估计也可以。反正最后用的线段树,普通的区间增,单点查询。

    一开始还担心复杂度,不过还行的感觉,每次二分都要重新建树,这里就是 logN*N*logN,对二分结果进行判断,需要单点查询以及区间更新,这里是2*logN*logN,所以总的就是

    logN*logN*(N+2)的复杂度,N最大为10的五次方,logN不超过20

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #define lson rt<<1,l,mid
    #define rson rt<<1|1,mid+1,r
    #define LL __int64
    using namespace std;
    const int  N = 100000+10;
    int n,m,w;
    LL d[N<<2],flag[N<<2],A[N];
    LL mini;
    void up(int rt)
    {
        d[rt]=min(d[rt<<1],d[rt<<1|1]);
    }
    void build(int rt,int l,int r)
    {
        flag[rt]=0;
        if (l>=r){
            d[rt]=A[l];
            //cout<<l<<" "<<A[l]<<endl;
            return;
        }
        int mid=(l+r)>>1;
        build(lson);
        build(rson);
        up(rt);
    }
    void pushdown(int rt,int l,int r)
    {
        if (flag[rt]==0) return;
        d[rt<<1]+=flag[rt];
        d[rt<<1|1]+=flag[rt];
        flag[rt<<1]+=flag[rt];
        flag[rt<<1|1]+=flag[rt];
        flag[rt]=0;
    }
    LL query(int loc,int rt,int l,int r)
    {
        if (l==r) return d[rt];
        int mid=(l+r)>>1;
        pushdown(rt,l,r);
        if (loc<=mid) return query(loc,lson);
        else return query(loc,rson);
    }
    void update(LL val,int L,int R,int rt,int l,int r)
    {
        if (L<=l && r<=R){
            d[rt]+=val;
            flag[rt]+=val;
            return;
        }
        pushdown(rt,l,r);
        int mid=(l+r)>>1;
        if (L<=mid) update(val,L,R,lson);
        if (R>mid) update(val,L,R,rson);
        up(rt);
    }
    bool judge(LL x)
    {
        LL tmp=m;
        for (int i=1;i<=n;i++){
            LL now=query(i,1,1,n);
           // cout<<"Test "<<i<<endl;
           // cout<<now<<endl;
            if (now<x){
               if (x-now>tmp) return 0;
               else {
                 update(x-now,i,min(i+w-1,n),1,1,n);
                 tmp-=x-now;
               }
            }
        }
        return 1;
    }
    int main()
    {
        while (scanf("%d%d%d",&n,&m,&w)!=EOF)
        {
            mini=-1;
            for (int i=1;i<=n;i++){
                scanf("%I64d",&A[i]);
                //cout<<i<<" "<<A[i]<<endl;
                if (mini==-1) mini=A[i];
                else mini=min(A[i],mini);
            }
            LL L=mini,R=mini+(LL)m,mid;
            while (L<R){
                build(1,1,n);
                mid=R-(R-L)/2;
               // cout<<L<<" "<<mid<<" "<<R<<endl;
                if (judge(mid)) {L=mid;}
                else {R=mid-1;}
            }
            printf("%I64d
    ",L);
        }
        return 0;
    
    }
    

      

  • 相关阅读:
    js中'1'到1的转换
    js类型判断
    docker安装mysql5.7
    HMM隐马尔可夫模型学习
    [python] wgs84转为gcj02坐标
    python经纬度转enu坐标
    Centos7开放及查看端口
    设计模式笔记
    npm 全面介绍
    Yarn 安装与使用详细介绍
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3932989.html
Copyright © 2020-2023  润新知