六月集训(第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;
}
};
东方欲晓,莫道君行早。

浙公网安备 33010602011771号