Vases and Flowers (线段树 + 二分)

题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=4614
题意:

有n个花瓶,标号0 ~ n−1。m个操作,
1AF,表示从A位置开始插FF朵花,遇到有花的花瓶跳过。到最后一个花瓶都还有花剩余,丢弃剩下的花。
2AB,表示将区间[A,B]内的花瓶全部清空。(A≤B)(A≤B)
对于每个11操作,输出第一个和最后一个插花的位置,如果一朵花都插不了,输出‘Can not put any one.’;对于每个2操作,输出区间[A,B]内被清空的花瓶的数量。

思路:
我们这里建树的时候,标号为(1 - n)但是题目给出的标号为0 - n -1
而且我们标记为插花为1,插花后为0
对于操作2,就是求区间和,然后用r - l + 1 - 区间和就可以了
对于操作1,我第一时间想的就是递归到树的底层进行操作,但是这样应该时间会挺长的,可以先把 起点 ->n中间空花瓶数量求出来,如果 == 0就输出Can not put any one.
如果不为0,
就二分查找从起点开始能恰好将第一朵花插入的位置,以及最后一朵花插入的位置,也即是找到一个区间,其空间,恰好可以将满足要插入花的数量,最后更新一下区间内空花瓶 减少的数量

/*1代表该花瓶为空,0表示该花瓶已经插花*/

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int N=5e4+10;

int sum[N*4],lazy[4*N];
//lazy[rt] == 0表示这个区间全被插满花,1为全空

void PushUp(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void Pushdown(int rt,int ln,int rn)
{
    if(lazy[rt] != -1){
        //因为要修改满足条件的区间,这个区间不全为1就是全为0
        //所以只需要记录这个满足条件的区间被清成0或者1即可
        lazy[rt<<1] = lazy[rt];
        lazy[rt<<1|1] = lazy[rt];
        sum[rt<<1] = ln * lazy[rt];
        sum[rt<<1|1] = rn * lazy[rt];
        lazy[rt] = -1;
    }
}

void Update(int ql,int qr,int val,int l,int r,int rt)
{
    if(ql <= l&&r <= qr){
        /*这里lazy直接赋值标记在于:
        1、如果是插花,这个区间一等是恰好能够把要插入的花完全放进去
        这样如果这个区间全为空,那么就直接填满,如果这个区间有被分割的
        区间可以满足要插入花的数量,那么操作完成后,整个区间也会被花插满
        所以,直接默认整个区间全部清成0即可
        2、如果这个是清除花,肯定是要整个区间全部变成1
        故,对于情况1、2我们只需要标记这个区间整体清成0或者1即可*/
        lazy[rt] = val;
        sum[rt] = (r - l + 1) * val;
        return ;
    }
    int m = (l + r)>>1;
    Pushdown(rt,m - l + 1,r - m);
    if(ql <= m){
        Update(ql,qr,val,l,m,rt<<1);
    }
    if(qr > m){
        Update(ql,qr,val,m + 1,r,rt<<1|1);
    }
    PushUp(rt);
}

int Query(int ql,int qr,int l,int r,int rt)
{
    if(ql <= l&&r <= qr){
        return sum[rt];
    }
    int m = (r + l)>>1;
    Pushdown(rt,m - l + 1,r - m);
    int ans = 0;
    if(ql <= m){
        ans += Query(ql,qr,l,m,rt<<1);
    }
    if(qr > m){
        ans += Query(ql,qr,m + 1,r,rt<<1|1);
    }
    return ans;
}

//二分查找满足可以将一定数量花全部插入的最小区间,即恰好把给定的花全部插入
int Search(int x,int n,int f_num)
{
    int l = x,r = n,ans = -1;
    while(l <= r){
        int mid = (r + l)>>1;
        int num = Query(x,mid,1,n,1);
        if(num >= f_num){
            ans = mid;
            r = mid - 1;
        }
        else{
            l = mid + 1;
        }
    }
    return ans;
}

void Build(int l,int r,int rt)
{
    sum[rt] = r - l + 1;
    lazy[rt] = -1;
    if(l == r){
        return ;
    }
    int m = (r + l)>>1;
    Build(l,m,rt<<1);
    Build(m + 1,r,rt<<1|1);
}

int main()
{
    int t,n,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        Build(1,n,1);
        int cnt;
        while(m--){
            scanf("%d",&cnt);
            if(cnt == 1){
                int ql,qr,l,f_num;
                scanf("%d%d",&l,&f_num);
                l++;
                int num = Query(l,n,1,n,1);
                //最大区间插入花数量为0
                if(num == 0){
                    printf("Can not put any one.\n");
                }
                else{
                    ql = Search(l,n,1);
                    qr = Search(l,n,min(num,f_num));
                    //表示该区间已经插满花
                    Update(ql,qr,0,1,n,1);
                    printf("%d %d\n",ql - 1,qr - 1);
                }
            }
            else{
                int l,r;
                scanf("%d%d",&l,&r);
                l++,r++;
                int num = Query(l,r,1,n,1);
                printf("%d\n",r - l + 1 - num);
                Update(l,r,1,1,n,1);
            }
        }
        cout<<endl;
    }
    return 0;
}

换种二分形式

/*1代表该花瓶为空,0表示该花瓶已经插花*/

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int N=5e4+10;

int sum[N*4],lazy[4*N];
//lazy[rt] == 0表示这个区间全被插满花,1为全空

void PushUp(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void Pushdown(int rt,int ln,int rn)
{
    if(lazy[rt] != -1){
        //因为要修改满足条件的区间,这个区间不全为1就是全为0
        //所以只需要记录这个满足条件的区间被清成0或者1即可
        lazy[rt<<1] = lazy[rt];
        lazy[rt<<1|1] = lazy[rt];
        sum[rt<<1] = ln * lazy[rt];
        sum[rt<<1|1] = rn * lazy[rt];
        lazy[rt] = -1;
    }
}

void Update(int ql,int qr,int val,int l,int r,int rt)
{
    if(ql <= l&&r <= qr){
        /*这里lazy直接赋值标记在于:
        1、如果是插花,这个区间一等是恰好能够把要插入的花完全放进去
        这样如果这个区间全为空,那么就直接填满,如果这个区间有被分割的
        区间可以满足要插入花的数量,那么操作完成后,整个区间也会被花插满
        所以,直接默认整个区间全部清成0即可
        2、如果这个是清除花,肯定是要整个区间全部变成1
        故,对于情况1、2我们只需要标记这个区间整体清成0或者1即可*/
        lazy[rt] = val;
        sum[rt] = (r - l + 1) * val;
        return ;
    }
    int m = (l + r)>>1;
    Pushdown(rt,m - l + 1,r - m);
    if(ql <= m){
        Update(ql,qr,val,l,m,rt<<1);
    }
    if(qr > m){
        Update(ql,qr,val,m + 1,r,rt<<1|1);
    }
    PushUp(rt);
}

int Query(int ql,int qr,int l,int r,int rt)
{
    if(ql <= l&&r <= qr){
        return sum[rt];
    }
    int m = (r + l)>>1;
    Pushdown(rt,m - l + 1,r - m);
    int ans = 0;
    if(ql <= m){
        ans += Query(ql,qr,l,m,rt<<1);
    }
    if(qr > m){
        ans += Query(ql,qr,m + 1,r,rt<<1|1);
    }
    return ans;
}

//二分查找满足可以将一定数量花全部插入的最小区间,即恰好把给定的花全部插入
int Search(int x,int n,int f_num)
{
    int l = x,r = n,ans = -1;
    while(l <  r){
        int mid = (r + l)>>1;
        int num = Query(x,mid,1,n,1);
        if(num >= f_num){
            ans = mid;
            r = mid;
        }
        else{
            l = mid + 1;
        }
    }
    return l;
}

void Build(int l,int r,int rt)
{
    sum[rt] = r - l + 1;
    lazy[rt] = -1;
    if(l == r){
        return ;
    }
    int m = (r + l)>>1;
    Build(l,m,rt<<1);
    Build(m + 1,r,rt<<1|1);
}

int main()
{
    int t,n,m;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        Build(1,n,1);
        int cnt;
        while(m--){
            scanf("%d",&cnt);
            if(cnt == 1){
                int ql,qr,l,f_num;
                scanf("%d%d",&l,&f_num);
                l++;
                int num = Query(l,n,1,n,1);
                //最大区间插入花数量为0
                if(num == 0){
                    printf("Can not put any one.\n");
                }
                else{
                    ql = Search(l,n,1);
                    qr = Search(l,n,min(num,f_num));
                    //表示该区间已经插满花
                    Update(ql,qr,0,1,n,1);
                    printf("%d %d\n",ql - 1,qr - 1);
                }
            }
            else{
                int l,r;
                scanf("%d%d",&l,&r);
                l++,r++;
                int num = Query(l,r,1,n,1);
                printf("%d\n",r - l + 1 - num);
                Update(l,r,1,1,n,1);
            }
        }
        cout<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值