线段树+离散化:磁盘文件操作-CSP202112-4

题目描述

csp202112-4

思路分析

写这个题的时候对线段树还不是很熟,因此先写了写简单的板子题,见线段树那些事
离散化的方法参考了100% 数据——离散化+线段树的实现。由于题目给出的修改和查询操作是预先已知的(操作本身的定义独立于程序运行状态),因此可以使用离线的离散化方法对数据进行处理,避免MLE。文章中也有动态开点线段树的方法,暂时还没有学习。

整体思路流程
  • 首先读入并保存所有的操作,读的过程中根据操作类型保存操作涉及的区间边界坐标,注意由于切割了的区间会产生左端点-1的值和右端点+1的值,因此需要将这些坐标也保存
  • 然后执行离散化操作。离散化操作首先需要对保存的“有用坐标”进行排序(sort)、去重(unique),然后再对保存的所有操作进行映射,这里使用lower_bound执行二分查找(论熟练使用STL的重要性……)。
  • 对离散化的数据进行建树。
  • 遍历所有的操作进行线段树的区间修改、区间查询即可。
线段树有关细节
  • 节点结构问题:本文的实现使用了如下的数据结构,相对较为简洁。
    struct FILE_BLOCK {
        int l, r;
        int x;  // value: -1e9~1e9, 2e9: mixed
        int id; // id: 1~n(<2e5), 2e9: mixed
        int state;  // FREE, OCCUPIED, ZOMBIE, 2e9: mixed
    };
    
    • 其中:l和r存储了该节点表示的区间左右界。虽然这个界可以在递归中给出,但保存的好处是减少递归中的函数调用参数,空间换时间,实测效果非常好。
    • x,id,state表示当前值、占用id和状态,引入了一个MIXED状态表示当前节点表示范围的某个值不唯一。
    • 需要注意在pushdown的时候只能下放不是MIXED的值。
  • 写递归函数的时候尽量简洁,减少特判和出口情况,避免考虑不全的特判造成的错误。
    • 比如update_test函数的递归非法状态出口条件是 fb[curr].state == OCCUPIED && fb[curr].id != MIXED,如果漏掉对 id 是否是 MIXED 的判断的话,会寄。

AC代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <assert.h>
using namespace std;

#define FREE 0
#define OCCUPIED 1
#define ZOMBIE 2

#define N_MAX 200005
#define MIXED 2000000020
#define FULL (-2000000020)

struct FILE_BLOCK {
    int l, r;
    int x;  // value: -1e9~1e9, 2e9: mixed
    int id; // id: 1~n(<2e5), 2e9: mixed
    int state;  // FREE, OCCUPIED, ZOMBIE, 2e9: mixed
};
struct OPRT {
    OPRT(int t, int i, int ll, int rr, int xx, int pp) : type(t), id(i), l(ll), r(rr), x(xx), p(pp) {}
    int type, id, l, r, x, p;
};

int n, m, k;
vector<FILE_BLOCK> fb(N_MAX << 4);
vector<OPRT> oprt;
vector<int> coordinates;

int discretization() {
    coordinates.push_back(0);   // let coordinates begin with 1
    sort(coordinates.begin(), coordinates.end());
    m = unique(coordinates.begin(), coordinates.end()) - coordinates.begin();   // 1~m
    coordinates.resize(m);
    for (auto & op : oprt) {
        switch (op.type) {
            case 0: case 1: case 2:
                op.l = lower_bound(coordinates.begin(), coordinates.end(), op.l) - coordinates.begin();
                op.r = lower_bound(coordinates.begin(), coordinates.end(), op.r) - coordinates.begin();
                break;
            case 3: default:
                op.p = lower_bound(coordinates.begin(), coordinates.end(), op.p) - coordinates.begin();
                break;
        }
    }
    return 0;
}

void pushup(int curr) {
    if (fb[curr<<1].id == fb[curr<<1|1].id) fb[curr].id = fb[curr<<1].id;
    else fb[curr].id = MIXED;
    if (fb[curr<<1].x == fb[curr<<1|1].x) fb[curr].x = fb[curr<<1].x;
    else fb[curr].x = MIXED;
    if (fb[curr<<1].state == fb[curr<<1|1].state) fb[curr].state = fb[curr<<1].state;
    else fb[curr].state = MIXED;
}
/* f5 */
void build(int curr, int l, int r){
    fb[curr].l = l;
    fb[curr].r = r;
    if (l == r) return;
    else {
        int mid = l+((r-l)>>1);     // 用减法不用加法,避免爆int
        build(curr<<1, l, mid);
        build((curr<<1)+1, mid+1, r);
        pushup(curr);
    }
}
void pushdown(int curr) {
    if (fb[curr].r != fb[curr].l) {
        if(fb[curr].id != MIXED) fb[curr << 1].id = fb[curr << 1 | 1].id = fb[curr].id;
        if(fb[curr].state != MIXED) fb[curr << 1].state = fb[curr << 1 | 1].state = fb[curr].state;
        if(fb[curr].x != MIXED) fb[curr << 1].x = fb[curr << 1 | 1].x = fb[curr].x;
    }
}
/* Interval change */
/* update_test: Find the rightmost available to_r */
int update_test(int curr, int to_l, int id) {
    if (fb[curr].state == FREE || fb[curr].state == ZOMBIE || fb[curr].id == id) return fb[curr].r;
    else if (fb[curr].state == OCCUPIED && fb[curr].id != MIXED) return FULL;   // debug: state = mixed, maybe itself
    else {  // state == mixed
        int mid = fb[curr].l+((fb[curr].r-fb[curr].l)>>1);
        pushdown(curr);
        if (to_l <= mid) {
            int left_r = update_test(curr<<1, to_l, id);
            if (left_r < mid) return left_r;
            int right_r = update_test(curr<<1|1, to_l, id);
            return right_r == FULL ? left_r : right_r;
        }
        else {
            return update_test(curr<<1|1, to_l, id);
        }
    }
};
void update_ic(int curr, int to_l, int to_r, int x, int id) {
    if (fb[curr].l == to_l && fb[curr].r == to_r) { // 刚好覆盖
        fb[curr].state = OCCUPIED;
        fb[curr].id = id;
        fb[curr].x = x;
        return;
    }
    int mid = fb[curr].l+((fb[curr].r-fb[curr].l)>>1);
    pushdown(curr);
    if (to_l <= mid) update_ic(curr<<1, to_l, min(to_r, mid), x, id);
    if (to_r > mid) update_ic(curr<<1|1, max(to_l, mid+1), to_r, x, id);
    pushup(curr);
};
/* Interval deletion */
bool deletion_test (int curr, int to_l, int to_r, int id) {
    if (fb[curr].l == to_l && fb[curr].r == to_r) { // 只在完全匹配的时候作判断
        if (fb[curr].state == OCCUPIED && fb[curr].id == id) return true;
        else return false;
    }
    int mid = fb[curr].l+((fb[curr].r-fb[curr].l)>>1);
    pushdown(curr);
    bool avai = true;
    if (to_l <= mid) avai &= deletion_test(curr<<1, to_l, min(to_r, mid), id);
    if (to_r > mid) avai &= deletion_test(curr<<1|1, max(to_l, mid+1), to_r, id);
    return avai;
}
void deletion (int curr, int to_l, int to_r, int id) {
    if (fb[curr].l == to_l && fb[curr].r == to_r) {
        fb[curr].state = ZOMBIE;
        return;
    }
    int mid = fb[curr].l+((fb[curr].r-fb[curr].l)>>1);
    pushdown(curr);
    if (to_l <= mid) deletion(curr<<1, to_l, min(to_r, mid), id);
    if (to_r > mid) deletion(curr<<1|1, max(to_l, mid+1), to_r, id);
    pushup(curr);
}
/* Interval recovery */
bool recover_test (int curr, int to_l, int to_r, int id) {
    if (fb[curr].l == to_l && fb[curr].r == to_r) {
        if (fb[curr].state == ZOMBIE && fb[curr].id == id) return true;
        else return false;
    }
    int mid = fb[curr].l+((fb[curr].r-fb[curr].l)>>1);
    pushdown(curr);
    bool avai = true;
    if (to_l <= mid) avai &= recover_test(curr<<1, to_l, min(to_r, mid), id);
    if (to_r > mid) avai &= recover_test(curr<<1|1, max(to_l, mid+1), to_r, id);
    return avai;
}
void recover (int curr, int to_l, int to_r, int id) {
    if (fb[curr].l == to_l && fb[curr].r == to_r) {
        fb[curr].state = OCCUPIED;
        return;
    }
    int mid = fb[curr].l+((fb[curr].r-fb[curr].l)>>1);
    pushdown(curr);
    if (to_l <= mid) recover(curr<<1, to_l, min(to_r, mid), id);
    if (to_r > mid) recover(curr<<1|1, max(to_l, mid+1), to_r, id);
    pushup(curr);
}
/* query */
int query (int curr, int p) {
    if (fb[curr].l == fb[curr].r) return curr;
    else if (fb[curr].state != MIXED && fb[curr].id != MIXED && fb[curr].x != MIXED) return curr;
    else {
        int mid = fb[curr].l+((fb[curr].r-fb[curr].l)>>1);
        pushdown(curr);
        if (p <= mid) return query(curr<<1, p);
        else return query(curr<<1|1, p);
    }
}

int main() {
    scanf("%d%d%d", &n, &m, &k);
    int type=0, id=0, l=0, r=0, x=0, p=0;
    for (int i = 0; i < k; i++) {
        scanf("%d", &type);
        switch (type) {
            case 0:
                scanf("%d%d%d%d", &id, &l, &r, &x);
                oprt.emplace_back(type, id, l, r, x, 0);
                break;
            case 1:
                scanf("%d%d%d", &id, &l, &r);
                oprt.emplace_back(type, id, l, r, 0, 0);
                break;
            case 2:
                scanf("%d%d%d", &id, &l, &r);
                oprt.emplace_back(type, id, l, r, 0, 0);
                break;
            case 3:
                scanf("%d", &p);
                oprt.emplace_back(type, 0, 0, 0, 0, p);
                break;
            default:
                break;
        }
        if (type == 0 || type == 1 || type == 2) {
            coordinates.push_back(l);
            coordinates.push_back(r);
            if (l != 1) coordinates.push_back(l-1);     // 注意离散化要加入前后的点
            if (r != m) coordinates.push_back(r+1);
        }
        else {
            coordinates.push_back(p);
        }
    }
    /* 离散化 */
    discretization();
    /* 建树 */
    build(1, 1, m-1);
    int query_index;
    for (auto & op : oprt) {
        switch (op.type) {
            case 0:
                op.r = min(op.r, update_test(1, op.l, op.id));
                if (op.r != FULL) update_ic(1, op.l, op.r, op.x, op.id);
                printf("%d\n", op.r==FULL?-1:coordinates[op.r]);
                break;
            case 1:
                if (deletion_test(1, op.l, op.r, op.id)) {
                    deletion(1, op.l, op.r, op.id);
                    printf("OK\n");
                }
                else {
                    printf("FAIL\n");
                }
                break;
            case 2:
                if (recover_test(1, op.l, op.r, op.id)) {
                    recover(1, op.l, op.r, op.id);
                    printf("OK\n");
                }
                else printf("FAIL\n");
                break;
            case 3:
                query_index = query(1, op.p);
                if (fb[query_index].state != OCCUPIED) printf("%d %d\n", 0, 0);
                else printf("%d %d\n", fb[query_index].id, fb[query_index].x);
                break;
            default:
                break;
        }
    }
    return 0;
}
  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值