线段树(区间修改 + 区间查询) + 二分:Vases and Flowers

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

分析:

此题唯一需要注意到的是,当我们进行操作1去找第一个和最后一个的时候,可以使用二分找答案的方式进行求解。

线段树中sum:表示区间L~R中空花瓶的总数。

而lazy则表示懒标记。用于区间修改,变为1或者变为0.

首先我们将空花瓶的时候定义为1. 所以在刚开始build()的时候,初始化他们全部为1.

当我们使用操作2,扔花的时候,就是区间修改为0

而当我们使用操作1,种花的时候,就是区间修改为1.

操作1的时候,要求出区间L~R中种了的花的数量。先用query()查询总和,就是总共的空花瓶数。然后L~R的总花瓶数 - 空花瓶。

 然后将区间修改为1.modify()操作。

操作2的时候,就要用到二分的操作。

二分找到第一个 总和 >= 1的节点下标(query()查询操作)。就是初始种花坐标

二分找到第一个 为 min(总空瓶,f)的坐标,因为可能f大于总空瓶数,作为最终种花位置。

代码实现:

# include <iostream>
using namespace std;

const int N = 50011;

struct Node
{
    int l,r;
    int sum; // l~r区间内空花瓶个数
    int lazy; //懒标记
}edgs[N * 4];

int t;

int n,m;

void pushup(int u)
{
    edgs[u].sum = edgs[2 * u].sum + edgs[2 * u + 1].sum;
}

void pushdown(int u)
{
    if(edgs[u].lazy != -1)
    {
        edgs[2 * u].lazy = edgs[u].lazy;
        edgs[2 * u + 1].lazy = edgs[u].lazy;
        edgs[2 * u].sum = (edgs[2 * u].r - edgs[2 *u].l + 1) * edgs[u].lazy;
        edgs[2 * u + 1].sum = (edgs[2 * u + 1].r - edgs[2 * u + 1].l +1) * edgs[u].lazy;
        edgs[u].lazy = -1;
    }
}

void build(int u , int l ,int r)
{
    edgs[u].l = l , edgs[u].r = r , edgs[u].lazy = -1;
    if(l == r)
    {
        edgs[u].sum = 1;
        return;
    }
    else
    {
        int mid = (edgs[u].l + edgs[u].r) / 2;
        build(2 * u , l , mid);
        build(2 * u + 1 , mid +1 , r);
        pushup(u);
    }
}

int query(int u , int l , int r)
{
    if(l <= edgs[u].l && edgs[u].r <= r)
    {
        return edgs[u].sum;
    }
    else
    {
        pushdown(u);
        int mid = (edgs[u].l + edgs[u].r) / 2;
        int ans = 0;
        if(l <= mid)
        {
            ans += query(2 * u , l , r);
        }
        if(r > mid)
        {
            ans += query(2 * u + 1, l , r);
        }
        return ans;
    }
}

void modify(int u , int l , int r , int k)
{
    if(l <= edgs[u].l && edgs[u].r <= r)
    {
        edgs[u].lazy = k;
        edgs[u].sum = (edgs[u].r - edgs[u].l + 1) * k;
    }
    else
    {
        pushdown(u);
        int mid = (edgs[u].l + edgs[u].r) / 2;
        if(l <= mid)
        {
            modify(2 * u , l , r , k);
        }
        if(r > mid)
        {
            modify(2 * u + 1 , l , r , k);
        }
        pushup(u);
    }
}

// 下标为0 ~ n - 1

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&m);
        int k;
        build(1,0,n - 1);
        for(int j = 1 ; j <= m ; j++)
        {
            scanf("%d",&k);
            if(k == 2) //查l,r区间插入了多少花
            {
                int l,r;
                scanf("%d %d",&l,&r);
                printf("%d\n",r - l + 1 - query(1,l,r));
                modify(1,l,r,1);
            }
            else
            {
                int a,f; // a为开始位置,f为花的数量
                scanf("%d %d",&a,&f); // 从a开始放f个
                if(query(1,a,n - 1) == 0) // 如果为0,说明全部都有花,则输出Can not put any one.
                {
                    printf("Can not put any one.\n");
                }
                else
                {
                    int l1 = a , r1 = n - 1; // 左区间,右区间,找边界, <= f的第一个值


                    while(l1 < r1)
                    {
                        int mid = (l1 + r1)/2;
                        if( query(1,a,mid) > 0) // 如果a~mid存在空的,则区间就在a~mid
                        {
                            r1 = mid;
                        }
                        else
                        {
                            l1 = mid + 1;
                        }
                    }
                    printf("%d ",l1);


                    /*
                     // 为什么不能使用 <= 进行二分呢?比如现在到i的时候为可以种花,恰好为f个,而后面被中了花,则我们最后找到的下标是后面被中了花的部分。
                    l1 = a , r1 = n - 1;
                    while(l1 < r1) // 二分
                    {
                        int mid = (l1 + r1 + 1) / 2;
                        if( query(1,a,mid) <= f) //a ~ mid种花,如果值 >= f说明肯定可以,因为1代表空,可以放
                        {
                            l1 = mid;
                        }
                        else
                        {
                            r1 = mid - 1;
                        }
                    }
                    // 最后的终点就是l1
                    printf("%d\n",l1);

                    */
                    
                    l1 = a , r1 = n - 1;
                    while(l1 < r1) // 二分
                    {
                        int mid = (l1 + r1) / 2;
                        if( query(1,a,mid) >= min(query(1,a,n-1),f) ) //a ~ mid种花,如果值 >= f说明肯定可以,因为1代表空,可以放
                        {
                            r1 = mid;
                        }
                        else
                        {
                            l1 = mid + 1;
                        }
                    }

                    printf("%d\n",l1);
                    
                    modify(1,a,l1,0); // a~l1都被种树了。
                }
            }
        }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值