HDU4614 Vases and Flowers 题解

发布时间 2024-01-07 17:23:52作者: Martian148

Question

HDU4614 Vases and Flowers

\(n\) 只花瓶,一只花瓶中只能插一朵花,Alice 经常收到很多花并插到花瓶中,她也经常清理花瓶

  • 1 A F 表示收到了 \(F\) 朵花,从第 \(A\) 只花瓶开始插,如果花瓶中原来有花,就跳过去插下一只花瓶,如果插到最后的花瓶还没有插完就丢弃,输出插入花瓶的第一个位置和最后一个位置
  • 2 A B 表示清理从 \(A\)\(B\) 的花瓶,输出清理了几朵花

Solution

一个比较典的线段树二分

用线段树,定义线段树的 "区间" ,表示这个区间已经插了花的花瓶数量

对于 \(2\) 操作,很明显就是一个区间求和 + 区间修改,先查询 \([A,B]\) 的区间和,然后把 \([A,B]\) 都赋为 \(1\)

对于 \(1\) 操作,我们先二分 \([A,n]\) 的第一个 \(0\) 的位置 \(pos1\),然后二分查找 \([pos1,n]\) 的第 \(F\)\(0\) 的位置 \(pos2\)

这里的二分可以直接二分,用 \(query()\) 来查找 \([L,R]\)\(1\) 的个数,也可以直接在线段树上二分

前者的时间复杂度是 \(O(m \log ^2 n)\) ,后者的时间复杂度是 \(O(m \log \log n)\)

Code

在值域上二分

#include<bits/stdc++.h>
using namespace std;

struct Seg_Tree{
    int n;
    vector<int> t,tag; // t 表示这个区间内花瓶的插了几个了,如果 tag=1 就表示已经插满了花了,tag=0 表示全部清零

    Seg_Tree(int n){this->n=n;t.assign(n<<2,0);tag.assign(n<<2,-1);}

    void push_up(int x){t[x]=t[x<<1]+t[x<<1|1];}

    void add_tag(int x,int l,int r,int val){
        if(val==0)
            t[x]=0,tag[x]=0;
        if(val==1)
            t[x]=(r-l+1),tag[x]=1;
    }

    void push_down(int x,int l,int r){
        if(tag[x]!=-1){
            int mid=(l+r)>>1;
            add_tag(x<<1,l,mid,tag[x]);
            add_tag(x<<1|1,mid+1,r,tag[x]);
            tag[x]=-1;
        }
    }

    void update(int x,int l,int r,int ql,int qr,int val){ //给 ql-qr 全部赋值为 val
        if(ql<=l&&r<=qr){add_tag(x,l,r,val);return ;}
        push_down(x,l,r);
        int mid=(l+r)>>1;
        if(ql<=mid) update(x<<1,l,mid,ql,qr,val);
        if(qr>mid)  update(x<<1|1,mid+1,r,ql,qr,val);
        push_up(x);
    }

    int query(int x,int l,int r,int ql,int qr){  //询问 ql-qr 一共有多少个 1
        if(ql<=l&&r<=qr) return t[x];
        push_down(x,l,r);
        int mid=(l+r)>>1,ret=0;
        if(ql<=mid) ret+=query(x<<1,l,mid,ql,qr);
        if(qr>mid)  ret+=query(x<<1|1,mid+1,r,ql,qr);
        return ret;
    }

    int find(int ql,int qr,int k) {  //寻找 ql-qr 第 k 个 0 的位置
        if(ql>qr) return -1;
        if(ql==qr) return qr;
        int mid=(ql+qr)>>1;
        int tmp_sum=query(1,1,n,ql,mid);
        if((mid-ql+1)-tmp_sum>=k)
            return find(ql,mid,k);
        else
            return find(mid+1,qr,(k-((mid-ql+1)-tmp_sum)));
    }
};

void solve(){
    int n,m;scanf("%d%d",&n,&m);
    Seg_Tree T(n);

    while(m--){
        int op,A,B;scanf("%d%d%d",&op,&A,&B);
        if(op==1){
            A++;
            int tmp_sum=T.query(1,1,n,A,n);
            if(tmp_sum==(n-A+1)) {printf("Can not put any one.\n");continue;}
            if((n-A+1)-tmp_sum < B) B=(n-A+1)-tmp_sum;
            int pos1=T.find(A,n,1);
            int pos2=T.find(pos1,n,B);
            if(pos2==-1) pos2=n;
            printf("%d %d\n",pos1-1,pos2-1);
            T.update(1,1,n,pos1,pos2,1);
        }
        else{
            A++;B++;
            printf("%d\n",T.query(1,1,n,A,B));
            T.update(1,1,n,A,B,0);
        }
    }
    printf("\n");
}

int main(){
    freopen("4614.in","r",stdin);
    freopen("4614.out","w",stdout);
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;
}

在线段树上二分

#include<bits/stdc++.h>
using namespace std;

struct Seg_Tree{
    int n;
    vector<int> t,tag; // t 表示这个区间内花瓶的插了几个了,如果 tag=1 就表示已经插满了花了,tag=0 表示全部清零

    Seg_Tree(int n){this->n=n;t.assign(n<<2,0);tag.assign(n<<2,-1);}

    void push_up(int x){t[x]=t[x<<1]+t[x<<1|1];}

    void add_tag(int x,int l,int r,int val){
        if(val==0)
            t[x]=0,tag[x]=0;
        if(val==1)
            t[x]=(r-l+1),tag[x]=1;
    }

    void push_down(int x,int l,int r){
        if(tag[x]!=-1){
            int mid=(l+r)>>1;
            add_tag(x<<1,l,mid,tag[x]);
            add_tag(x<<1|1,mid+1,r,tag[x]);
            tag[x]=-1;
        }
    }

    void update(int x,int l,int r,int ql,int qr,int val){ //给 ql-qr 全部赋值为 val
        if(ql<=l&&r<=qr){add_tag(x,l,r,val);return ;}
        push_down(x,l,r);
        int mid=(l+r)>>1;
        if(ql<=mid) update(x<<1,l,mid,ql,qr,val);
        if(qr>mid)  update(x<<1|1,mid+1,r,ql,qr,val);
        push_up(x);
    }

    int query(int x,int l,int r,int ql,int qr){  //询问 ql-qr 一共有多少个 1
        if(ql<=l&&r<=qr) return t[x];
        push_down(x,l,r);
        int mid=(l+r)>>1,ret=0;
        if(ql<=mid) ret+=query(x<<1,l,mid,ql,qr);
        if(qr>mid)  ret+=query(x<<1|1,mid+1,r,ql,qr);
        return ret;
    }

    int find(int x,int l,int r,int ql,int qr,int k) {  //寻找 ql-qr 第 k 个 0 的位置
        if(l==r) return l;
        push_down(x,l,r);
        int mid=(l+r)>>1;
        
        if(ql>mid) return find(x<<1|1,mid+1,r,ql,qr,k); //如果查询区间不包括左区间,之间查询右区间

        int left_sum=query(x<<1,l,mid,ql,mid);   // 左区间 1 的个数
        if(k <= mid-ql+1-left_sum)   
            return find(x<<1,l,mid,ql,mid,k);  // 在左边
        else 
            return find(x<<1|1,mid+1,r,mid+1,qr,k-(mid-ql+1-left_sum)); //在右边
    }
};

void solve(){
    int n,m;scanf("%d%d",&n,&m);
    Seg_Tree T(n);

    while(m--){
        int op,A,B;scanf("%d%d%d",&op,&A,&B);
        if(op==1){
            A++;
            int tmp_sum=T.query(1,1,n,A,n);
            if(tmp_sum==(n-A+1)) {printf("Can not put any one.\n");continue;}
            if((n-A+1)-tmp_sum < B) B=(n-A+1)-tmp_sum;
            int pos1=T.find(1,1,n,A,n,1);
            int pos2=T.find(1,1,n,pos1,n,B);
            if(pos2==-1) pos2=n;
            printf("%d %d\n",pos1-1,pos2-1);
            T.update(1,1,n,pos1,pos2,1);
        }
        else{
            A++;B++;
            printf("%d\n",T.query(1,1,n,A,B));
            T.update(1,1,n,A,B,0);
        }
    }
    printf("\n");
}

int main(){
    // freopen("4614.in","r",stdin);
    // freopen("46140.out","w",stdout);
    int T;scanf("%d",&T);
    while(T--) solve();
    return 0;
}