From e6e6b222f8fbe0758fdfeb595b30cd8ecea0ef7f Mon Sep 17 00:00:00 2001 From: gameloader Date: Thu, 26 Sep 2024 13:23:25 +0800 Subject: [PATCH] leetcode update --- content/posts/leetcode.md | 117 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/content/posts/leetcode.md b/content/posts/leetcode.md index 1646780..de0765e 100644 --- a/content/posts/leetcode.md +++ b/content/posts/leetcode.md @@ -14103,3 +14103,120 @@ public: } }; ``` + +## day212 2024-09-26 + +### 729. My Calendar I + +You are implementing a program to use as your calendar. We can add a new event if adding the event will not cause a double booking. + +A double booking happens when two events have some non-empty intersection (i.e., some moment is common to both events.). + +The event can be represented as a pair of integers start and end that represents a booking on the half-open interval [start, end), the range of real numbers x such that start <= x < end. + +Implement the MyCalendar class: + +MyCalendar() Initializes the calendar object. +boolean book(int start, int end) Returns true if the event can be added to the calendar successfully without causing a double booking. Otherwise, return false and do not add the event to the calendar. + +![09266fkystmmNuG2](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/09266fkystmmNuG2.png) + +### 题解 + +本题涉及到区间问题,可以使用线段树。在熟悉线段树结构的情况下本题非常容易解决,只需查询book中出现的区间在线段树中查询得到的区间和是否为0即可知道该区间是否被覆盖过,如果为0说明区间完全没有被覆盖则可以预订,并更新对应区间的值,这里可以给区间所有值加一(在线段树中这个操作并不会真的将全部节点都加1,而是会存在懒操作,只要在覆盖这个区间的节点内加入标记记录了存在加1操作)。非0则区间可能被部分覆盖,即有冲突,则不能预订。 + +关键的部分在线段树的实现,这里我们使用动态开点的线段树并使用懒标记。 + +### 代码 + +```cpp +class MyCalendar { +private: + struct Node { + Node* left; + Node* right; + int val; + int lazy; + Node() : left(nullptr), right(nullptr), val(0), lazy(0) {} + }; + + Node* root; + const int MAX_RANGE = 1e9; + + void pushDown(Node* node, int start, int end) { + if (!node->left) node->left = new Node(); + if (!node->right) node->right = new Node(); + if (node->lazy) { + int mid = start + (end - start) / 2; + node->left->val += node->lazy; + node->left->lazy += node->lazy; + node->right->val += node->lazy; + node->right->lazy += node->lazy; + node->lazy = 0; + } + } + + int query(Node* node, int start, int end, int l, int r) { + if (l <= start && end <= r) return node->val; + pushDown(node, start, end); + int mid = start + (end - start) / 2; + int res = 0; + if (l <= mid) res += query(node->left, start, mid, l, r); + if (r > mid) res += query(node->right, mid + 1, end, l, r); + return res; + } + + void update(Node* node, int start, int end, int l, int r, int val) { + if (l <= start && end <= r) { + node->val += val; + node->lazy += val; + return; + } + pushDown(node, start, end); + int mid = start + (end - start) / 2; + if (l <= mid) update(node->left, start, mid, l, r, val); + if (r > mid) update(node->right, mid + 1, end, l, r, val); + node->val = max(node->left->val, node->right->val); + } + +public: + MyCalendar() { + root = new Node(); + } + + bool book(int start, int end) { + if (query(root, 0, MAX_RANGE, start, end - 1) > 0) { + return false; + } + update(root, 0, MAX_RANGE, start, end - 1, 1); + return true; + } +}; + +``` + +### 总结 + +本题也可以使用简单的二分法求解,将book中的每个区间加入到一个有序set中。对当前book区间在set中通过二分快速查找到第一个已经存在的区间的结束时间大于该区间的开始和结束时间(其余情况或者和该区间有重合,或者位于该区间前,不会影响该区间的插入)。再判断找到的这个区间的开始时间和当前book区间的结束时间的大小,如果小于则有覆盖,大于等于则无覆盖,说明当前book区间可以预订,将其插入到这个有序set中。代码如下 + +```cpp +class MyCalendar { + struct Node { + int start, end; + Node(int start, int end) : start(start), end(end) {} + bool operator<(const Node& other) const { return other.start >= end; } + }; + set s; + +public: + MyCalendar() {} + + bool book(int start, int end) { + auto it = s.lower_bound({start, end}); + if (it != s.end() && it->start < end) + return false; + s.insert({start, end}); + return true; + } +}; +```