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.\n");
                }else{
                    update(1,1,n,rr,jl,1);
                    printf("%d %d\n",rr-1,jl-1);
                }
            }else if(op==2){
                printf("%d\n",qurey(1,1,n,x+1,y+1));
                update(1,1,n,x+1,y+1,0);
           }
        }
        printf("\n");
    }   
}

解法二:

线段树上二分,复杂度\(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.\n");
                    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\n",inx2-1,inx1-1);
            }
            else if(op==2){
                printf("%d\n",(b-a+1)-qurey(1,1,n,a+1,b+1));
                update(1,1,n,a+1,b+1,1);
            }
            
        }
        printf("\n");
    }
}
posted @ 2020-11-26 23:32  高级牛头人  阅读(96)  评论(0编辑  收藏  举报