POJ 3667 Hotel & HDU 2871 Memory Control 线段树区间合并

本文详细解析了线段树在区间操作上的应用,包括如何处理区间覆盖与取消覆盖的操作,通过具体题目如POJ3667和HDU2871来阐述算法实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

POJ 3667

参考

poj 3667 Hotel ——Titanium

题意

一条线段长度为 n ,初始未被覆盖。进行 两种操作 m 次:
1. 询问 最左边的 未被覆盖的 长度 D 的 区域的左端点,并覆盖这段区域;
2. 清除 [x,x+d1] 区域的覆盖。

思路

线段树上记录的信息还是老套路,左边连续的最大值,右边连续的最大值,一整段中的最大值;清除操作也是老套路, Lazy Tag 搞一搞。

关键就是询问操作怎么办。
可以认为与 hdu 2795 Billboard 转化 线段树 区间最大值 有异曲同工之处,那道题也是尽量向左找。

于是乎:先看 lson ,不满足的话就看中间拼起来的区域,再不满足再看 rson ,就解决了。

那么找到什么时候停呢?
一开始想着再记录一个信息,即这一段的最左边的端点,那么当其中的最大连续长度就等于查询长度的时候就可以不用继续往下找,而能直接返回这个纪录的值了。
后来发现不好维护,只好作罢= =
那么就一路向下找到叶子节点吧 _(:з」∠)_

Code

#include <cstdio>
#include <iostream>
#define lson (rt<<1)
#define rson (rt<<1|1)
#define maxn 50010
using namespace std;
struct node {
    int l, r, mx, ml, mr, p, flag, len;
}tr[maxn * 4];
void build(int rt, int l, int r) {
    tr[rt].l = l, tr[rt].r = r, tr[rt].len = tr[rt].ml = tr[rt].mr = tr[rt].mx = r-l+1;
    tr[rt].flag = -1;
    if (l == r) return;
    int mid = l + r >> 1;
    build(lson, l, mid); build(rson, mid+1, r);
}
void push_down(int rt) {
    if (tr[rt].flag != -1) {
        tr[lson].flag = tr[rson].flag = tr[rt].flag;
        tr[lson].ml = tr[lson].mr = tr[lson].mx = tr[lson].flag * tr[lson].len;
        tr[rson].ml = tr[rson].mr = tr[rson].mx = tr[rson].flag * tr[rson].len;
        tr[rt].flag = -1;
    }
}
void push_up(int rt) {
    tr[rt].ml = tr[lson].ml; if (tr[lson].ml == tr[lson].len) tr[rt].ml += tr[rson].ml;
    tr[rt].mr = tr[rson].mr; if (tr[rson].mr == tr[rson].len) tr[rt].mr += tr[lson].mr;
    tr[rt].mx = max(max(tr[lson].mx, tr[rson].mx), tr[lson].mr+tr[rson].ml);
}
int query(int rt, int len) {
    if (tr[rt].l == tr[rt].r && tr[rt].mx == len) return tr[rt].l;
    push_down(rt);
    if (tr[lson].mx >= len) return query(lson, len);
    else if (tr[lson].mr+tr[rson].ml >= len) return tr[lson].r-tr[lson].mr+1;
    else if (tr[rson].mx >= len) return query(rson, len);
    else return 0;
}
void modify(int rt, int l, int r, int x) {
    if (tr[rt].l == l && tr[rt].r == r) {
        tr[rt].ml = tr[rt].mr = tr[rt].mx = x * tr[rt].len;
        tr[rt].p = x ? tr[rt].l : 0;
        tr[rt].flag = x;
        return;
    }
    push_down(rt);
    int mid = tr[rt].l+tr[rt].r>>1;
    if (r <= mid) modify(lson, l, r, x);
    else if (l > mid) modify(rson, l, r, x);
    else { modify(lson, l, mid, x); modify(rson, mid+1, r, x); }
    push_up(rt);
}
int n, m;
void work() {
    build(1, 1, n);
    while (m--) {
        int x, d, p, len;
        scanf("%d%d", &x, &d);
        if (x == 1) {
            int p = query(1, d);
            if (p) modify(1, p, d+p-1, 0);
            printf("%d\n", p);
        }
        else {
            scanf("%d", &len);
            modify(1, d, min(d+len-1, n), 1);
        }
    }
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF) work();
    return 0;
}

HDU 2871

题意

一条线段长度为 n ,初始未被覆盖。进行 四种操作 m 次:
1.New D :询问 最左边的 未被覆盖的 长度 D 的 区域的左端点,并用一条线段覆盖这段区域;
2.Free x :清除覆盖 x 位置的线段;
3.Get x:打印第 x 条线段的左右端点;
4.Reset:清除所有线段。

思路

New 操作和上一题中差不多。
Free 的话一开始以为要写和 hdu 1540 Tunnel Warfare 线段树 / set 这道题差不多的,后来看了一些博客大家都是用的 vector 搞定的 Free Get 操作 0.0
(个人一直以为 vector 和数组效率差不多…就很害怕)
至于 Reset 就是一整段的 Free

Code

#include <bits/stdc++.h>
#define maxn 50010
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
struct node {
    int l,r,ml,mr,mx,len,flag;
}tr[maxn * 4];
vector<pair<int,int> > v;
bool cmp(pair<int, int> u, pair<int, int> v) { return u.first < v.first; }
void build(int rt, int l, int r) {
    tr[rt].l = l, tr[rt].r = r, tr[rt].len = r-l+1, tr[rt].flag = -1;
    tr[rt].ml = tr[rt].mr = tr[rt].mx = tr[rt].len;
    if (l == r) return;
    int mid = tr[rt].l + tr[rt].r >> 1;
    build(lson, l, mid); build(rson, mid+1, r);
}
void push_down(int rt) {
    if (tr[rt].flag != -1) {
        tr[lson].flag = tr[rson].flag = tr[rt].flag;
        tr[lson].ml = tr[lson].mr = tr[lson].mx = tr[rt].flag ? tr[lson].len : 0;
        tr[rson].ml = tr[rson].mr = tr[rson].mx = tr[rt].flag ? tr[rson].len : 0;
        tr[rt].flag = -1;
    }
}
void push_up(int rt) {
    tr[rt].ml = tr[lson].ml; if (tr[lson].ml == tr[lson].len) tr[rt].ml += tr[rson].ml;
    tr[rt].mr = tr[rson].mr; if (tr[rson].mr == tr[rson].len) tr[rt].mr += tr[lson].mr;
    tr[rt].mx = max(max(tr[lson].mx, tr[rson].mx), tr[lson].mr+tr[rson].ml);
}
int query(int rt, int len) {
    if (tr[rt].l == tr[rt].r && tr[rt].mx == len) return tr[rt].l;
    push_down(rt);
    if (tr[lson].mx >= len) return query(lson, len);
    else if (tr[lson].mr + tr[rson].ml >= len) return tr[lson].r-tr[lson].mr+1;
    else if (tr[rson].mx >= len) return query(rson, len);
    else return 0;
}
void modify(int rt, int l, int r, int x) {
    if (tr[rt].l == l && tr[rt].r == r) {
        tr[rt].ml = tr[rt].mr = tr[rt].mx = x ? tr[rt].len : 0;
        tr[rt].flag = x;
        return;
    }
    push_down(rt);
    int mid = tr[rt].l + tr[rt].r >> 1;
    if (r <= mid) modify(lson, l, r, x);
    else if (l > mid) modify(rson, l, r, x);
    else { modify(lson, l, mid, x); modify(rson, mid+1, r, x); }
    push_up(rt);
}
int m, n;
void work() {
    build(1, 1, n);
    v.clear();
    while (m--) {
        char s[10];
        scanf("%s", s);
        char c = s[0];
        if (c == 'R') {
            modify(1, 1, n, 1); v.clear();
            printf("Reset Now\n");
            continue;
        }
        int x;
        scanf("%d", &x);
        if (c == 'N') {
            int p = query(1, x);
            if (p) {
                modify(1, p, p+x-1, 0);
                pair<int, int> pr(p, p+x-1);
                auto it = lower_bound(v.begin(), v.end(), pr, cmp);
                v.insert(it, pr);
                printf("New at %d\n", p);
            }
            else printf("Reject New\n");
        }
        else if (c == 'F') {
            pair<int, int> pr(x, x);
            auto it = upper_bound(v.begin(), v.end(), pr, cmp);
            if (it == v.begin()) printf("Reject Free\n");
            else {
                --it;
                if (it->second >= x) {
                    modify(1, it->first, it->second, 1);
                    printf("Free from %d to %d\n", it->first, it->second);
                    v.erase(it);
                }
                else printf("Reject Free\n");
            }
        }
        else if (c == 'G') {
            if (x > v.size()) printf("Reject Get\n");
            else printf("Get at %d\n", v[x-1].first);
        }
    }
    printf("\n");
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF) work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值