六月集训(第24天)—线段树

@

线段树

1. 731. 我的日程安排表 II

    思路:
        利用线段树维护区间的增删        mask利用位运算记录区间安排的任务数,mask的第i位为1,则代表出现了i次
        lazy代表安排的任务数,作为位移的次数,用于更新mask和线段树的标记更新

class MyCalendarTwo {
    struct TreeNode {
        TreeNode *left, *right;
        int lazy;
        int mask;
        TreeNode() {
            lazy = 0;
            mask = 1;
            left = right = NULL;
        }
    };
    TreeNode *root;
public:
    MyCalendarTwo() {
        root = NULL;
    }
    
    void insert(TreeNode *&tn, int l, int r, int start, int end, int lazy) {
        if (end < l || r < start) return ;  // 要插入的区间在可插入的区间之外
        if (tn == NULL) tn = new TreeNode();
        tn->lazy += lazy;
        tn->mask <<= lazy;

        if (start <= l && r <= end) {   /* 将插入区间完全覆盖的区间打上标记 */
            tn->lazy++;
            tn->mask <<= 1;
            return ;
        }

        int nowlazy = tn->lazy;
        tn->lazy = 0;

        // 拆分为更小的区间,直到插入时间完全标记好
        int mid = l + ((r - l) >> 1);
        insert(tn->left, l, mid, start, end, nowlazy);
        insert(tn->right, mid + 1, r, start, end, nowlazy);

        tn->mask = 0;   //  标记下放
        if (tn->left) tn->mask |= tn->left->mask;
        if (tn->right) tn->mask |= tn->right->mask;
    }

    void query(TreeNode *&tn, int l, int r, int start, int end, int lazy, int &mask) {
        if (tn == NULL) {   /* 查询区间没有安排任务 */
            if (lazy == 0) {    // 不插入
                return ;
            }
            tn = new TreeNode();    // 线段树还不存在该区间的任务,新建一个结点
        }

        tn->lazy += lazy;
        tn->mask <<= lazy;

        if (end < l || r < start) return ;  /* 任务安排在不合理的时间,返回上一层,去找合理的时间区间 */

        /* 区间内安排的任务数相关的标记下放 */
        if (start <= l && r <= end) {   
            mask |= tn->mask;
            return ;
        }
        int nowlazy = tn->lazy;
        tn->lazy = 0;
        tn->mask = 0;

        int mid = l + ((r - l) >> 1);
        query(tn->left, l, mid, start, end, nowlazy, mask);
        query(tn->right, mid + 1, r, start, end, nowlazy, mask);

        // 递归回溯更新上一层区间的任务数
        if (tn->left) tn->mask |= tn->left->mask;
        if (tn->right) tn->mask |= tn->right->mask;
    }

    bool book(int start, int end) {
        int mask = 0;
        query(root, 0, 1e9, start, end - 1, 0, mask);
        if ((mask >> 2) >= 1) {	/* 第3位为1,至少同一个区间安排了三件任务,不满足题意 */
            return false;
        } 
        insert(root, 0, 1e9, start, end - 1, 0);
        return true;
    }
};

/**
 * Your MyCalendarTwo object will be instantiated and called as such:
 * MyCalendarTwo* obj = new MyCalendarTwo();
 * bool param_1 = obj->book(start,end);
 */

2. 699. 掉落的方块

    思路:
        同第一题, 只不过这次利用线段树维护区间最大值
        遍历所有方块,先查询方块要插入的区间内的最大值,然后将当前最大值+方块的边长,放入线段树更新结果。

class Solution {
    struct TreeNode {
        TreeNode *left, *right;
        int lazy, maxn;

        TreeNode() {
            left = right = NULL;
            lazy = 0;
            maxn = 0;
        }
    };

    void insert(TreeNode *&tn, int l, int r, int start, int end, int lazy, int maxn) {
        if (tn == NULL) tn = new TreeNode();
        if (lazy > tn->lazy) {
            tn->lazy = lazy;
            tn->maxn = maxn;
        }

        if (end < l || r < start) return ;  // 不在可落入的区间则返回

        if (start <= l && r <= end) {
            tn->lazy = maxn;
            tn->maxn = maxn;
            return ;
        }
        int nowlazy = tn->lazy;
        tn->lazy = 0;
        tn->maxn = 0;

        int mid = l + ((r - l) >> 1);
        insert(tn->left, l, mid, start, end, nowlazy, maxn);
        insert(tn->right, mid + 1, r, start, end, nowlazy, maxn);

        if (tn->left && tn->left->maxn > tn->maxn) tn->maxn = tn->left->maxn;
        if (tn->right && tn->right->maxn > tn->maxn) tn->maxn = tn->right->maxn;
    }

    void query(TreeNode *&tn, int l, int r, int start, int end, int lazy, int &maxn) {
        if (tn == NULL) {
            if (lazy == 0) return ; // 不用插入
            tn = new TreeNode();    //  需要插入但是线段树中没有该结点,则创建一个新结点
        }
        if (lazy > tn->lazy) {
            tn->lazy = lazy;
            tn->maxn = lazy;
        }

        if (end < l || r < start) return ;

        if (start <= l && r <= end) {
            if (tn->maxn > maxn) maxn = tn->maxn;
            return ;
        }
        int nowlazy = tn->lazy;
        tn->lazy = 0;
        tn->maxn = 0;

        int mid = l + ((r - l) >> 1);
        query(tn->left, l, mid, start, end, nowlazy, maxn);
        query(tn->right, mid + 1, r, start, end, nowlazy, maxn);

        if (tn->left && tn->left->maxn > tn->maxn) tn->maxn = tn->left->maxn;
        if (tn->right && tn->right->maxn > tn->maxn) tn->maxn = tn->right->maxn;
    }
public:
    vector<int> fallingSquares(vector<vector<int>>& positions) {
        vector<int> ret;
        TreeNode *root = NULL;
        int positions_size = positions.size(), i;
        int l, r, maxn;
        for (i = 0; i < positions_size; ++i) {
            l = positions[i][0];
            r = l + positions[i][1] - 1;    //  最右边还可以放一个方块
            maxn = 0;
            query(root, 0, 1e9, l, r, 0, maxn); // 找到当前要放入的区间的最大值
            insert(root, 0, 1e9, l, r, 0, maxn + positions[i][1]);  // 将要放入的方块的边长加上最大值更新线段树
            ret.push_back(root->maxn);
        }
        return ret;
    }
};
posted @ 2022-06-24 09:32  番茄元  阅读(26)  评论(0)    收藏  举报