Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 18aca29

Browse files
✨feat: add 749、731、2213
1 parent a2694a0 commit 18aca29

File tree

3 files changed

+166
-13
lines changed

3 files changed

+166
-13
lines changed

‎LeetCode/2211-2220/2213. 由单个字符重复的最长子字符串(困难).md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class Solution {
136136
}
137137
}
138138
```
139-
* 时间复杂度:$O(m * \log{n})$
139+
* 时间复杂度:$O(m\log{n})$
140140
* 空间复杂度:$O(n)$
141141

142142
---

‎LeetCode/731-740/731. 我的日程安排表 II(中等).md‎

Lines changed: 163 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
### 题目描述
22

3-
这是 LeetCode 上的 **[731. 我的日程安排表 II](https://leetcode-cn.com/problems/my-calendar-ii/solution/by-ac_oier-okkc/)** ,难度为 **中等**
3+
这是 LeetCode 上的 **[731. 我的日程安排表 II](https://leetcode.cn/problems/my-calendar-ii/solution/by-ac_oier-a1b3/)** ,难度为 **中等**
44

55
Tag : 「线段树(动态开点)」、「线段树」
66

@@ -40,7 +40,7 @@ MyCalendar.book(25, 55); // returns true
4040

4141
---
4242

43-
### 线段树(动态开点)
43+
### 线段树(动态开点)- 估点
4444

4545
[729. 我的日程安排表 I](https://leetcode-cn.com/problems/my-calendar-i/solution/by-ac_oier-1znx/) 几乎完全一致,只需要将对「线段树」所维护的节点信息进行调整即可。
4646

@@ -56,24 +56,21 @@ MyCalendar.book(25, 55); // returns true
5656

5757
但对于本题而言,由于「强制在线」的原因,我们无法进行「离散化」,同时值域大小达到 1ドルe9$ 级别,因此如果我们想要使用「线段树」进行求解,只能采取「动态开点」的方式进行。
5858

59-
动态开点的优势在于,不需要事前构造空树,而是在插入操作 `update` 和查询操作 `query` 时根据访问需要进行「开点」操作。由于我们不保证查询和插入都是连续的,因此对于父节点 $u$ 而言,我们不能通过 `u << 1``u << 1 | 1` 的固定方式进行访问,而要将节点 $tr[u]$ 的左右节点所在 `tr` 数组的下标进行存储,分别记为 `ls``rs` 属性。对于 $tr[u].ls = 0$ 和 $tr[u].rs = 0$ 则是代表子节点尚未被创建,当需要访问到它们,而又尚未创建的时候,则将其进行创建。
59+
动态开点的优势在于,不需要事前构造空树,而是在插入操作 `add` 和查询操作 `query` 时根据访问需要进行「开点」操作。由于我们不保证查询和插入都是连续的,因此对于父节点 $u$ 而言,我们不能通过 `u << 1``u << 1 | 1` 的固定方式进行访问,而要将节点 $tr[u]$ 的左右节点所在 `tr` 数组的下标进行存储,分别记为 `ls``rs` 属性。对于 $tr[u].ls = 0$ 和 $tr[u].rs = 0$ 则是代表子节点尚未被创建,当需要访问到它们,而又尚未创建的时候,则将其进行创建。
6060

61-
由于存在「懒标记」,线段树的插入和查询都是 $\log{n}$ 的,因此我们在单次操作的时候,最多会创建数量级为 $\log{n}$ 的点,因此空间复杂度为 $O(m\log{n}),ドル而不是 $O(4 \times n),ドル而开点数的预估需不能仅仅根据 $\log{n}$ 来进行,还要对常数进行分析,才能得到准确的点数上界。
61+
由于存在「懒标记」,线段树的插入和查询都是 $\log{n}$ 的,因此我们在单次操作的时候,最多会创建数量级为 $\log{n}$ 的点,因此空间复杂度为 $O(m\log{n}),ドル而不是 $O(4 \times n),ドル而开点数的预估需不能仅仅根据 $\log{n}$ 来进行,还要对常熟进行分析,才能得到准确的点数上界。
6262

63-
动态开点相比于原始的线段树实现,本质仍是使用「满二叉树」的形式进行存储,只不过是按需创建区间,如果我们是按照连续段进行查询或插入,最坏情况下仍然会占到 4ドル * n$ 的空间,因此盲猜 $\log{n}$ 的常数在 4ドル$ 左右,保守一点可以直接估算到 6ドル,ドル因此我们可以估算点数为 6ドル * m * \log{n},ドル其中 $n = 1e9$ 和 $m = 1e3$ 分别代表值域大小和查询次数。
63+
动态开点相比于原始的线段树实现,本质仍是使用「满二叉树」的形式进行存储,只不过是按需创建区间,如果我们是按照连续段进行查询或插入,最坏情况下仍然会占到 4ドル \times n$ 的空间,因此盲猜 $\log{n}$ 的常数在 4ドル$ 左右,保守一点可以直接估算到 6ドル,ドル因此我们可以估算点数为 6ドル \times m \times \log{n},ドル其中 $n = 1e9$ 和 $m = 1e3$ 分别代表值域大小和查询次数。
6464

6565
当然一个比较实用的估点方式可以「尽可能的多开点数」,利用题目给定的空间上界和我们创建的自定义类(结构体)的大小,尽可能的多开( `Java` 的 128ドルM$ 可以开到 5ドル \times 10^6$ 以上)。
6666

67-
代码:
67+
Java 代码:
6868
```Java
6969
class MyCalendarTwo {
7070
class Node {
71-
// ls 和 rs 分别代表当前节点的左右子节点在 tr 的下标
72-
// add 为懒标记
73-
// max 为当前区间的最大值
7471
int ls, rs, add, max;
7572
}
76-
int N = (int)1e9, M = 1000010, cnt = 1;
73+
int N = (int)1e9, M = 120010, cnt = 1;
7774
Node[] tr = new Node[M];
7875
void update(int u, int lc, int rc, int l, int r, int v) {
7976
if (l <= lc && rc <= r) {
@@ -123,6 +120,162 @@ class MyCalendarTwo {
123120
}
124121
}
125122
```
123+
TypeScript 代码:
124+
```TypeScript
125+
class TNode {
126+
ls: number = 0; rs: number = 0
127+
max:number = 0; add: number = 0;
128+
}
129+
class MyCalendarTwo {
130+
N = 1e9; M = 120010; cnt = 1
131+
tr: TNode[] = new Array<TNode>(this.M)
132+
query(u: number, lc: number, rc: number, l: number, r: number): number {
133+
if (l <= lc && rc <= r) return this.tr[u].max;
134+
this.pushdown(u)
135+
let mid = lc + rc >> 1, ans = 0
136+
if (l <= mid) ans = Math.max(ans, this.query(this.tr[u].ls, lc, mid, l, r))
137+
if (r > mid) ans = Math.max(ans, this.query(this.tr[u].rs, mid + 1, rc, l, r))
138+
return ans
139+
}
140+
update(u: number, lc: number, rc: number, l: number, r: number, v: number): number {
141+
if (l <= lc && rc <= r) {
142+
this.tr[u].add += v
143+
this.tr[u].max += v
144+
return
145+
}
146+
this.pushdown(u)
147+
let mid = lc + rc >> 1
148+
if (l <= mid) this.update(this.tr[u].ls, lc, mid, l, r, v)
149+
if (r > mid) this.update(this.tr[u].rs, mid + 1, rc, l, r, v)
150+
this.pushdup(u)
151+
}
152+
pushdown(u: number): void {
153+
if (this.tr[u] == null) this.tr[u] = new TNode()
154+
if (this.tr[u].ls == 0) {
155+
this.tr[u].ls = ++this.cnt
156+
this.tr[this.tr[u].ls] = new TNode()
157+
}
158+
if (this.tr[u].rs == 0) {
159+
this.tr[u].rs = ++this.cnt
160+
this.tr[this.tr[u].rs] = new TNode()
161+
}
162+
const add = this.tr[u].add
163+
this.tr[this.tr[u].ls].add += add; this.tr[this.tr[u].rs].add += add
164+
this.tr[this.tr[u].ls].max += add; this.tr[this.tr[u].rs].max += add
165+
this.tr[u].add = 0
166+
}
167+
pushdup(u: number): void {
168+
this.tr[u].max = Math.max(this.tr[this.tr[u].ls].max, this.tr[this.tr[u].rs].max)
169+
}
170+
book(start: number, end: number): boolean {
171+
if (this.query(1, 1, this.N + 1, start + 1, end) >= 2) return false
172+
this.update(1, 1, this.N + 1, start + 1, end, 1)
173+
return true
174+
}
175+
}
176+
```
177+
* 时间复杂度:令 $n$ 为值域大小,本题固定为 1ドルe9,ドル线段树的查询和增加复杂度均为 $O(\log{n})$
178+
* 空间复杂度:令询问数量为 $m,ドル复杂度为 $O(m\log{n})$
179+
180+
---
181+
182+
### 线段树(动态开点)- 动态指针
183+
184+
利用「动态指针」实现的「动态开点」可以有效避免数组估点问题,更重要的是可以有效避免 `new` 大数组的初始化开销,对于 LC 这种还跟你算所有样例总时长的 OJ 来说,在不考虑 `static` 优化/全局数组优化 的情况下,动态指针的方式要比估点的方式来得好。
185+
186+
Java 代码:
187+
```Java
188+
class MyCalendarTwo {
189+
class Node {
190+
Node ls, rs;
191+
int max, add;
192+
}
193+
int N = (int)1e9;
194+
Node root = new Node();
195+
void update(Node node, int lc, int rc, int l, int r, int v) {
196+
if (l <= lc && rc <= r) {
197+
node.add += v;
198+
node.max += v;
199+
return ;
200+
}
201+
pushdown(node);
202+
int mid = lc + rc >> 1;
203+
if (l <= mid) update(node.ls, lc, mid, l, r, v);
204+
if (r > mid) update(node.rs, mid + 1, rc, l, r, v);
205+
pushup(node);
206+
}
207+
int query(Node node, int lc, int rc, int l, int r) {
208+
if (l <= lc && rc <= r) return node.max;
209+
pushdown(node);
210+
int mid = lc + rc >> 1, ans = 0;
211+
if (l <= mid) ans = query(node.ls, lc, mid, l, r);
212+
if (r > mid) ans = Math.max(ans, query(node.rs, mid + 1, rc, l, r));
213+
return ans;
214+
}
215+
void pushdown(Node node) {
216+
if (node.ls == null) node.ls = new Node();
217+
if (node.rs == null) node.rs = new Node();
218+
int add = node.add;
219+
node.ls.max += add; node.rs.max += add;
220+
node.ls.add += add; node.rs.add += add;
221+
node.add = 0;
222+
}
223+
void pushup(Node node) {
224+
node.max = Math.max(node.ls.max, node.rs.max);
225+
}
226+
public boolean book(int start, int end) {
227+
if (query(root, 0, N, start, end - 1) >= 2) return false;
228+
update(root, 0, N, start, end - 1, 1);
229+
return true;
230+
}
231+
}
232+
```
233+
TypeScript 代码:
234+
```TypeScript
235+
class TNode {
236+
ls: TNode = null; rs: TNode = null
237+
max: number = 0; add: number = 0
238+
}
239+
class MyCalendarTwo {
240+
root: TNode = new TNode()
241+
update(node: TNode, lc: number, rc: number, l: number, r: number, v: number): void {
242+
if (l <= lc && rc <= r) {
243+
node.add += v
244+
node.max += v
245+
return
246+
}
247+
this.pushdown(node)
248+
let mid = lc + rc >> 1
249+
if (l <= mid) this.update(node.ls, lc, mid, l, r, v)
250+
if (r > mid) this.update(node.rs, mid + 1, rc, l, r, v)
251+
this.pushup(node)
252+
}
253+
query(node: TNode, lc: number, rc: number, l: number, r: number): number {
254+
if (l <= lc && rc <= r) return node.max
255+
let mid = lc + rc >> 1, ans = 0
256+
this.pushdown(node)
257+
if (l <= mid) ans = this.query(node.ls, lc, mid, l, r)
258+
if (r > mid) ans = Math.max(ans, this.query(node.rs, mid + 1, rc, l, r))
259+
return ans
260+
}
261+
pushdown(node: TNode): void {
262+
if (node.ls == null) node.ls = new TNode()
263+
if (node.rs == null) node.rs = new TNode()
264+
const add = node.add
265+
node.ls.add += add; node.rs.add += add
266+
node.ls.max += add; node.rs.max += add
267+
node.add = 0
268+
}
269+
pushup(node: TNode): void {
270+
node.max = Math.max(node.ls.max, node.rs.max)
271+
}
272+
book(start: number, end: number): boolean {
273+
if (this.query(this.root, 0, 1e9, start, end - 1) >= 2) return false
274+
this.update(this.root, 0, 1e9, start, end - 1, 1)
275+
return true
276+
}
277+
}
278+
```
126279
* 时间复杂度:令 $n$ 为值域大小,本题固定为 1ドルe9,ドル线段树的查询和增加复杂度均为 $O(\log{n})$
127280
* 空间复杂度:令询问数量为 $m,ドル复杂度为 $O(m\log{n})$
128281

‎LeetCode/741-750/749. 隔离病毒(困难).md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ let g: number[][] = null
145145
let n: number = 0, m: number = 0
146146
let vis: boolean[][] = null
147147
const dirs: number[][] = [[1,0],[-1,0],[0,1],[0,-1]]
148-
function dfs(_x: number, _y: number, s1: Set<number>, s2: Set<number>): number {
148+
function search(_x: number, _y: number, s1: Set<number>, s2: Set<number>): number {
149149
let he = 0, ta = 0, ans = 0
150150
let d: Array<number> = new Array<number>()
151151
s1.add(_x * m + _y)
@@ -178,7 +178,7 @@ function getCnt(): number {
178178
for (let j = 0; j < m; j++) {
179179
if (g[i][j] == 1 && !vis[i][j]) {
180180
let s1 = new Set<number>(), s2 = new Set<number>()
181-
const b = dfs(i, j, s1, s2), a = s2.size
181+
const b = search(i, j, s1, s2), a = s2.size
182182
if (a > max) {
183183
max = a; ans = b
184184
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /