poj1823线段树

题意:

有人要住旅馆,然后住在一些连续的房间里;

那么让你求空闲的连续的旅馆的最大长度;


理解:

此题认真想想应该就是线段树了;

然后合并的是区间,而不是单独的值;

合并区间就有点麻烦了;

主要就是两个区间合并的时候要注意中间是否可以再组成一个区间;‘

做法就是记录此区间的两端没有住人的两端;

然后将此区间的最大值求出来;

之后向上合并;

注意合并时要判断中间是否可以连接起来;


代码如下:(线段树就是太难写了,不过思路清晰就好写了)


#include <cstdio>

#include <algorithm>
#include <vector>

using namespace std;

#define P pair<int, int>
#define x first
#define y second

struct node
{
    int len;
    int flag;
    P lt, rt; //左右两端的空闲区间
};

node tree[16000 * 5 + 10];
int up[16000 * 5 + 10];

node build(int l, int r, int rx)
{
    if (l == r)
    {
        tree[rx].len = 1;
        tree[rx].flag = 0;
        tree[rx].lt = tree[rx].rt = P(l, r);
        return tree[rx];
    }

    node n1 = build(l, (l + r) / 2, rx * 2);
    node n2 = build((l + r) / 2 + 1, r, rx * 2 + 1);
    tree[rx].len = n1.len + n2.len;
    tree[rx].flag = 0;
    tree[rx].lt = tree[rx].rt = P(n1.lt.x, n2.rt.y);

    return tree[rx];
}

void push_down(int l, int r, int rx)
{
    if (l != r)
    {
        up[rx * 2] = up[rx];
        up[rx * 2 + 1] = up[rx];
        if (up[rx] == 1)
        {
            tree[rx * 2].len = tree[rx * 2 + 1].len = 0;

            tree[rx * 2].flag = tree[rx * 2 + 1].flag = 1;

            tree[rx * 2].lt
                = tree[rx * 2].rt
                = tree[rx * 2 + 1].lt
                = tree[rx * 2 + 1].rt
                = P(-1, -1);
        }
        else
        {
            tree[rx * 2].len
                = (l + r) / 2 - l + 1;
            tree[rx * 2 + 1].len
                = r - (l + r) / 2 - 1 + 1;

            tree[rx * 2].flag = tree[rx * 2 + 1].flag = 0;

            tree[rx * 2].lt
                = tree[rx * 2].rt
                = P(l, (l + r) / 2);
            tree[rx * 2 + 1].lt
                = tree[rx * 2 + 1].rt
                = P((l + r) / 2 + 1, r);
        }
    }
    up[rx] = 0;
}

void update(int l, int r, int L, int R, int rx, int num)
{
    if (L > r || R < l) return ;

    if (L >= l && R <= r)
    {
        up[rx] = (num == 0 ? -1 : 1);
        if (num == 0)
        {
            tree[rx].len = R - L + 1;
            tree[rx].flag = 0;
            tree[rx].lt = tree[rx].rt = P(L, R);
        }
        else
        {
            tree[rx].len = 0;
            tree[rx].flag = 1;
            tree[rx].lt = tree[rx].rt = P(-1, -1);
        }
        return ;
    }

    if (up[rx])
    {
        push_down(L, R, rx);  //开始的时候push_down放错位置了,结果超时,坑
    }

    update(l, r, L, (L + R) / 2, rx * 2, num);
    node n1 = tree[rx * 2];
    update(l, r, (L + R) / 2 + 1, R, rx * 2 + 1, num);
    node n2 = tree[rx * 2 + 1];

    if (n1.flag != n2.flag)
    {
        tree[rx] = (n1.flag == 0 ? n1 : n2);
        return ;
    }

    if (n1.flag == n2.flag && n1.flag == 1)
    {
        tree[rx].len = 0;
        tree[rx].flag = 1;
        tree[rx].lt = tree[rx].rt = P(-1, -1);
        return ;
    }

    if (n1.rt.y + 1 == n2.lt.x)
    {
        tree[rx].len = max(n1.len, max(n2.len, n2.lt.y - n1.rt.x + 1));
        tree[rx].flag = 0;
        if (n1.lt.y < n1.rt.x)
        {
            tree[rx].lt = n1.lt;
        }
        else tree[rx].lt = P(n1.rt.x, n2.lt.y);

        if (n2.lt.y < n2.rt.x)
        {
            tree[rx].rt = n2.rt;
        }
        else tree[rx].rt = P(n1.rt.x, n2.lt.y);
        return ;
    }

    tree[rx].len = max(n1.len, n2.len);
    tree[rx].flag = 0;
    tree[rx].lt = n1.lt;
    tree[rx].rt = n2.rt;
    return ;
}

int main()
{
    int n, p;
    scanf("%d%d", &n, &p);
    build(1, n, 1);
    for (int i = 0; i < p; ++i)
    {
        int num;
        scanf("%d", &num);
        if (num == 3)
        {
            printf("%d\n", tree[1].len);
        }
        else if (num == 1)
        {
            int num1, num2;
            scanf("%d%d", &num1, &num2);
            update(num1, num1 + num2 - 1, 1, n, 1, 1);
        }
        else
        {
            int num1, num2;
            scanf("%d%d", &num1, &num2);
            update(num1, num1 + num2 - 1, 1, n, 1, 0);
        }
    }

    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值