线段树/区间合并(POJ 3667 Hotel )

本文深入探讨了一种使用段式树状数组进行优化的数据结构实现,特别关注于最大空余连续区间的查找与更新。通过详细的代码示例,讲解了如何在区间内快速查找最大连续空余区间的算法,并提供了完整的AC代码实现。

题目链接
这个题是集训是讲的模板题,但是我没(mei)听(you)懂(ting),于是做题时比较惨,卡了不少时间。最后想:

struct Node
{
    int l, r;
    int sum;//记录该区间内的最大空余连续区间
    int add;//延迟标记
    bool flag;//全空标记
    int ls, rs;//记录该区间中,最大空余前缀、后缀/
}tr[N<<2];

于是我们的pushup操作可以是:

void pushup(int p)
{
    tr[p].sum =max(max(tr[p<<1].sum, tr[p<<1|1].sum), tr[p<<1].rs + tr[p<<1|1].ls);
    //该区间的最大空余连续区间,应该是max(左孩子最大连续区间, 右孩子最大连续区间, 左孩子最大后缀+右孩子最大前缀)
    tr[p].ls = tr[p<<1].ls;
    tr[p].rs = tr[p<<1|1].rs;
    //默认该区间最大前缀为其左孩子最大前缀,后缀同理
    tr[p].flag = tr[p<<1].flag && tr[p<<1|1].flag;
    //左孩子右孩子均全空
    if (tr[p<<1].flag) tr[p].ls = tr[p<<1].sum + tr[p<<1|1].ls;
    if (tr[p<<1|1].flag) tr[p].rs = tr[p<<1|1].sum + tr[p<<1].rs;
    //如果左孩子全空,那么最大前缀还可能是:左孩子区间长度+右孩子前缀,
    //后缀同理
}

下面是ac代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
#include <cstdlib>
#include <queue>
#include <map>
#include <vector>
#define ll long long
using namespace std;
const int N =1e5+5;
struct Node
{
    int l, r;
    int sum;
    int add;
    bool flag;
    int ls, rs;
}tr[N<<2];
int mx;
void build(int p, int l, int r)
{
    mx = max(mx, p);
    tr[p].l = l; tr[p].r = r;
    tr[p].add = -1;
    tr[p].flag = 1;
    if (l == r)
    {
        tr[p].sum = tr[p].ls = tr[p].rs = 1;
        return;
    }
    int mid = (l + r) >>1;
    build(p<<1, l, mid);
    build(p<<1|1, mid+1, r);
    tr[p].ls = tr[p].rs = tr[p].sum = tr[p<<1].sum + tr[p<<1|1].sum;
}
void spread(int p)
{
    if (tr[p].add == -1) return;
    int l = p<<1, r = p<<1|1;
    tr[l].sum = tr[l].ls = tr[l].rs = tr[p].add * (tr[l].r - tr[l].l + 1);
    tr[r].sum = tr[r].ls = tr[r].rs = tr[p].add * (tr[r].r - tr[r].l + 1);
    tr[l].add = tr[r].add = tr[p].add;
    tr[l].flag = tr[r].flag = tr[p].add;
    tr[p].add = -1;
}
void pushup(int p)
{
    tr[p].sum =max(max(tr[p<<1].sum, tr[p<<1|1].sum), tr[p<<1].rs + tr[p<<1|1].ls);
    tr[p].ls = tr[p<<1].ls;
    tr[p].rs = tr[p<<1|1].rs;
    tr[p].flag = tr[p<<1].flag && tr[p<<1|1].flag;
    if (tr[p<<1].flag) tr[p].ls = tr[p<<1].sum + tr[p<<1|1].ls;
    if (tr[p<<1|1].flag) tr[p].rs = tr[p<<1|1].sum + tr[p<<1].rs;
}
void change(int p, int l, int r, bool flag)
{
    if (l <= tr[p].l && r >= tr[p].r)
    {
        tr[p].sum = tr[p].ls = tr[p].rs = flag * (tr[p].r - tr[p].l + 1);
        tr[p].add = flag;
        tr[p].flag = flag;
        return;
    }
    spread(p);
    int mid = (tr[p].r + tr[p].l) >> 1;
    if (l <= mid) change(p<<1, l, r, flag);
    if (r > mid) change(p<<1|1, l, r, flag);
    pushup(p);
}
int ask(int p, int k)
{
    if (tr[p].l == tr[p].r)
    {
        if (k > tr[p].sum) return 0;
        return tr[p].l;
    }
    int mid = (tr[p].l + tr[p].r) >> 1;
    spread(p);
    if (tr[p<<1].sum >= k) return ask(p<<1, k);
    if (tr[p<<1].rs + tr[p<<1|1].ls >= k) return mid - tr[p<<1].rs + 1;
    else if (tr[p<<1|1].sum >= k) return ask(p<<1|1, k);
    return 0;
}
void print()
{
    for (int i = 1; i <= mx; i++)
    {
        cout << tr[i].l << " " << tr[i].r << " " << tr[i].sum << endl;
        cout << tr[i].ls << " " << tr[i].rs << " " << tr[i].flag << endl;
        cout << "--------" << endl;
    }
    cout << "------------------------" << endl;
}
int main()
{
    int n, m;
    cin >> n >> m;
    build(1, 1, n);
   // print();
    while(m--)
    {
        int op;
        scanf("%d", &op);
        if (op == 1)
        {
            int k;
            scanf("%d", &k);
            int gg = ask(1, k);
            if (gg)
                change(1, gg, gg + k -1, 0);
        //    print();
            printf("%d\n", gg);
        }
        else
        {
            int l, r;
            scanf("%d%d", &l, &r);
            change(1, l, r +l - 1, 1);
         //   print();
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值