zoukankan      html  css  js  c++  java
  • Vases and Flowers(线段树+二分)

    Vases and Flowers(线段树+二分)

    HDU - 4614

    解法一:

    二分区间,线段区间查询空花瓶个数。复杂度(O(nlogn^2))

    由于区间越大,能空花瓶数越多,满足二分单调性。

    操作1:二分pos到n区间的,区间查询二分区间的空花瓶数,找到插花的第一个位置和最后一个位置,然后区间修改

    操作2:输出查询区间值,修改区间。

    #include <iostream>
    #include <cstring>
    #define lch 2*k
    #define rch 2*k+1
    #define mid (l+r)/2
    using namespace std;
    const int N=1e5+7;
    int t;
    int n,m,tree[4*N],tap[4*N];
    void init(int k,int l,int r){
        tap[k]=-1;
        if(l>=r){
            tree[k]=0;
            return;
        }
        init(lch,l,mid);
        init(rch,mid+1,r);
        tree[k]=tree[lch]+tree[rch];
    }
    
    void pushdown(int k,int zl,int zr,int yl,int yr){
        if(tap[k]!=-1){
            tree[lch]=tap[k]*(zr-zl+1);
            tree[rch]=tap[k]*(yr-yl+1);
            tap[lch]=tap[k];
            tap[rch]=tap[k];
            tap[k]=-1;
        }
    }
    
    void update(int k,int l,int r,int ql,int qr,int cost){
        if(ql>qr){
            return;
        }
        if(ql<=l&&r<=qr){
            tree[k]=cost*(r-l+1);
            tap[k]=cost;
            return;
        }
        pushdown(k,l,mid,mid+1,r);
        if(ql<=mid) update(lch,l,mid,ql,qr,cost);
        if(mid+1<=qr) update(rch,mid+1,r,ql,qr,cost);
    
        tree[k]=tree[lch]+tree[rch];
    
    }
    
    int qurey(int k,int l,int r,int ql,int qr){
    
        if(ql>qr){
            return 0;
        }
        if(ql<=l&&r<=qr){
            return tree[k];
        }
        pushdown(k,l,mid,mid+1,r);
        int sum1=0,sum2=0;
        if(ql<=mid) sum1=qurey(lch,l,mid,ql,qr);
        if(mid+1<=qr) sum2=qurey(rch,mid+1,r,ql,qr);
    
        return sum1+sum2;
    }
    
    bool judge(int ql,int qr,int key){
        int cnt=(qr-ql+1)-qurey(1,1,n,ql,qr);
    
        if(cnt>=key){
            return true;
        }else{
            return false;
        }
    }
    int main(){
        scanf("%d",&t);
        while(t--){
            scanf("%d %d",&n,&m);
            //memset(tree,0,sizeof(tree));
            //memset(tap,-1,sizeof(tap));
            init(1,1,n);
            while(m--){
                int op,x,y;
                scanf("%d %d %d",&op,&x,&y);   
                if(op==1){
                    y=min(y,(n-x)-qurey(1,1,n,x+1,n));
                    int jl=x+1,jr=n;
                    while(jl<=jr){
                        int md=(jl+jr)/2;
                        if(judge(x+1,md,y)){
                            jr=md-1;
                        }else{
                            jl=md+1;
                        }
                    }
                    int ll=x+1,rr=jl;
                    while(ll<=rr){
                        int md=(ll+rr)/2;
                        if(judge(md,jl,y)){
                            ll=md+1;
                        }else{
                            rr=md-1;
                        }
                    }
                    if(y==0){
                        printf("Can not put any one.
    ");
                    }else{
                        update(1,1,n,rr,jl,1);
                        printf("%d %d
    ",rr-1,jl-1);
                    }
                }else if(op==2){
                    printf("%d
    ",qurey(1,1,n,x+1,y+1));
                    update(1,1,n,x+1,y+1,0);
               }
            }
            printf("
    ");
        }   
    }
    

    解法二:

    线段树上二分,复杂度(O(nlogn))

    由题意可知,我们需要往(pos)位置插b朵花,也可能插少于(b)朵花,即为b朵花和(pos)(n)位置的空花瓶数取最小值。

    操作1:

    为了方便从(1)(n)线段树二分,我们查询(1)(pos-1)位置的空花瓶数,加上(pos)位置之后的需要插花的数量,设为(cnt1)

    在区间1到n线段树上二分查找从1开始到第cnt1空花瓶位置,设左子树的空花瓶数为(tem),若(tem)数大于或等于(cnt1),则往左子树查找,否则往右子树查找(cnt1-tem),找到第(cnt1)位置的空花瓶的下标值,即为最后插花的位置。

    查询开始插花的位置,即查询(pos)(n)的空花瓶数,为(cnt2),同理二分查找(n)开始到(cnt2)个空花瓶位置,即为开始插花的位置。

    最后区间修改。

    操作2:

    区间查询和修改。

    #include <iostream>
    #define lch 2*k
    #define rch 2*k+1
    #define mid (l+r)/2
    using namespace std;
    const int N=1e5+7;
    int tree[4*N],tap[4*N];
    int t,n,q;
    void init(int k,int l,int r){
        tap[k]=-1;
        if(l>=r){
            tree[k]=1;
            return;
        }
        init(lch,l,mid);
        init(rch,mid+1,r);
        tree[k]=tree[lch]+tree[rch];
    }
    void pushdown(int k,int zl,int zr,int yl,int yr){
        if(tap[k]!=-1){
            tree[lch]=tap[k]*(zr-zl+1);
            tree[rch]=tap[k]*(yr-yl+1);
            tap[lch]=tap[k];
            tap[rch]=tap[k];
            tap[k]=-1;
        }
    }
    void update(int k,int l,int r,int ql,int qr,int cost){
        if(ql>qr){
            return;
        }
        if(ql<=l&&r<=qr){
            tree[k]=cost*(r-l+1);
            tap[k]=cost;
            return;
        }
        pushdown(k,l,mid,mid+1,r);
        if(ql<=mid) update(lch,l,mid,ql,qr,cost);
        if(mid+1<=qr) update(rch,mid+1,r,ql,qr,cost);
        tree[k]=tree[lch]+tree[rch];
    }
    
    int qurey(int k,int l,int r,int ql,int qr){
        if(ql>qr){
            return 0;
        }
        if(ql<=l&&r<=qr){
            return tree[k];
        }
        pushdown(k,l,mid,mid+1,r);
        int sum1=0,sum2=0;
        if(ql<=mid) sum1=qurey(lch,l,mid,ql,qr);
        if(mid+1<=qr) sum2=qurey(rch,mid+1,r,ql,qr);
        return sum1+sum2;
    }
    
    int searchR(int k,int l,int r,int cnt){
        if(l>=r){
            return l;
        }
        pushdown(k,l,mid,mid+1,r);
        int tem=tree[lch];
        int inx=-1;
        if(tem>=cnt){
            inx=searchR(lch,l,mid,cnt);
        }else{
            inx=searchR(rch,mid+1,r,cnt-tem);
        }
        return inx;
    }
    
    int searchL(int k,int l,int r,int cnt){
        if(l>=r){
            return l;
        }
        pushdown(k,l,mid,mid+1,r);
        int tem=tree[rch];
        int inx=-1;
        if(tem>=cnt){
            inx=searchL(rch,mid+1,r,cnt);
            
        }else{
            inx=searchL(lch,l,mid,cnt-tem);
        }
        return inx;
    }
    int main(){
        scanf("%d",&t);
        while(t--){
            scanf("%d %d",&n,&q);
            init(1,1,n);
            while(q--){
                int op,a,b;
                scanf("%d %d %d",&op,&a,&b);
                
                if(op==1){
                    if(qurey(1,1,n,a+1,n)==0){
                        printf("Can not put any one.
    ");
                        continue;
                    }
                    int cnt1=min(qurey(1,1,n,1,n),b+qurey(1,1,n,1,a));
                    int inx1=searchR(1,1,n,cnt1);
                    int cnt2=qurey(1,1,n,a+1,n);
                    int inx2=searchL(1,1,n,cnt2);
                    update(1,1,n,inx2,inx1,0);
                    printf("%d %d
    ",inx2-1,inx1-1);
                }
                else if(op==2){
                    printf("%d
    ",(b-a+1)-qurey(1,1,n,a+1,b+1));
                    update(1,1,n,a+1,b+1,1);
                }
                
            }
            printf("
    ");
        }
    }
    
  • 相关阅读:
    5.7填数字游戏求解
    5.6判断回文数字
    5.5百钱买百鸡问题
    5.4三色球问题
    5.3哥德巴赫猜想的近似证明
    5.2求两个数的最大公约数和最小公倍数
    5.1舍罕王的失算
    4.19递归反向输出字符串
    Elasticsearch 安装
    linux 安装nginx步骤
  • 原文地址:https://www.cnblogs.com/kksk/p/14045392.html
Copyright © 2011-2022 走看看