52
52
53
53
<!-- 这里可写通用的实现逻辑 -->
54
54
55
- 这是一道 ** 离线思维 ** 的题目。
55
+ ** 方法一:离线查询 + 并查集 **
56
56
57
- ** 离线 ** 的意思是,一道题目会给出若干 query,而这些 query 会全部提前给出。也就是说,我们可以不必按照 query 的顺序依次对它们进行处理,而是可以按照另外某种顺序进行处理。与 ** 离线 ** 相对应的是 ** 在线 ** ,即所有 query 会依次给出,在返回第 k 个 query 的答案之前,不会获得第 k+1 个 query 。
57
+ 根据题目要求,我们需要对每个查询 $queries [ i ] $ 进行判断,即判断当前查询的两个点 $a$ 和 $b$ 之间是否存在一条边权小于等于 $limit$ 的路径 。
58
58
59
- 对于本题,可以转换为:将小于 limit 的所有边加入图中,判断此时 pj, qj 是否连通。可以用并查集来实现 。
59
+ 判断两点是否连通可以通过并查集来实现。另外,由于查询的顺序对结果没有影响,因此我们可以先将所有查询按照 $ limit$ 从小到大排序,所有边也按照边权从小到大排序 。
60
60
61
- 以下是并查集的几个常用模板 。
61
+ 然后对于每个查询,我们从边权最小的边开始,将边权严格小于 $limit$ 的所有边加入并查集,接着利用并查集的查询操作判断两点是否连通即可 。
62
62
63
- 模板 1——朴素并查集:
63
+ 时间复杂度 $O(m \times \log m + q \times \log q)$,其中 $m$ 和 $q$ 分别为边数和查询数。
64
64
65
- ``` python
66
- # 初始化,p存储每个点的父节点
67
- p = list (range (n))
65
+ 附并查集相关介绍以及常用模板:
68
66
69
- # 返回x的祖宗节点
70
- def find (x ):
71
- if p[x] != x:
72
- # 路径压缩
73
- p[x] = find(p[x])
74
- return p[x]
67
+ 并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的** 合并** 及** 查询** 问题。 它支持两种操作:
75
68
69
+ 1 . 查找(Find):确定某个元素处于哪个子集,单次操作时间复杂度 $O(\alpha(n))$
70
+ 1 . 合并(Union):将两个子集合并成一个集合,单次操作时间复杂度 $O(\alpha(n))$
76
71
77
- # 合并a和b所在的两个集合
78
- p[find(a)] = find(b)
79
- ```
72
+ 其中 $\alpha$ 为阿克曼函数的反函数,其增长极其缓慢,也就是说其单次操作的平均运行时间可以认为是一个很小的常数。
80
73
81
- 模板 2——维护 size 的并查集 :
74
+ 以下是并查集的常用模板,需要熟练掌握。其中 :
82
75
83
- ``` python
84
- # 初始化,p存储每个点的父节点,size只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量
76
+ - ` n ` 表示节点数
77
+ - ` p ` 存储每个点的父节点,初始时每个点的父节点都是自己
78
+ - ` size ` 只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量
79
+ - ` find(x) ` 函数用于查找 $x$ 所在集合的祖宗节点
80
+ - ` union(a, b) ` 函数用于合并 $a$ 和 $b$ 所在的集合
81
+
82
+ ``` python [sol1-Python3 模板]
85
83
p = list (range (n))
86
84
size = [1 ] * n
87
85
88
- # 返回x的祖宗节点
89
86
def find (x ):
90
87
if p[x] != x:
91
88
# 路径压缩
92
89
p[x] = find(p[x])
93
90
return p[x]
94
91
95
- # 合并a和b所在的两个集合
96
- if find(a) != find(b):
97
- size[find(b)] += size[find(a)]
98
- p[find(a)] = find(b)
92
+
93
+ def union (a , b ):
94
+ pa, pb = find(a), find(b)
95
+ if pa == pb:
96
+ return
97
+ p[pa] = pb
98
+ size[pb] += size[pa]
99
99
```
100
100
101
- 模板 3——维护到祖宗节点距离的并查集:
101
+ ``` java [sol1-Java 模板]
102
+ int [] p = new int [n];
103
+ int [] size = new int [n];
104
+ for (int i = 0 ; i < n; ++ i) {
105
+ p[i] = i;
106
+ size[i] = 1 ;
107
+ }
102
108
103
- ``` python
104
- # 初始化,p存储每个点的父节点,d[x]存储x到p[x]的距离
105
- p = list (range (n))
106
- d = [0 ] * n
109
+ int find(int x) {
110
+ if (p[x] != x) {
111
+ // 路径压缩
112
+ p[x] = find(p[x]);
113
+ }
114
+ return p[x];
115
+ }
107
116
108
- # 返回x的祖宗节点
109
- def find (x ):
110
- if p[x] != x:
111
- t = find(p[x])
112
- d[x] += d[p[x]]
113
- p[x] = t
117
+ void union(int a, int b) {
118
+ int pa = find(a), pb = find(b);
119
+ if (pa == pb) {
120
+ return ;
121
+ }
122
+ p[pa] = pb;
123
+ size[pb] += size[pa];
124
+ }
125
+ ```
126
+
127
+ ``` cpp [sol1-C++ 模板]
128
+ vector<int > p (n);
129
+ iota(p.begin(), p.end(), 0);
130
+ vector<int > size(n, 1);
131
+
132
+ int find(int x) {
133
+ if (p[ x] != x) {
134
+ // 路径压缩
135
+ p[ x] = find(p[ x] );
136
+ }
137
+ return p[ x] ;
138
+ }
139
+
140
+ void unite(int a, int b) {
141
+ int pa = find(a), pb = find(b);
142
+ if (pa == pb) return;
143
+ p[ pa] = pb;
144
+ size[ pb] += size[ pa] ;
145
+ }
146
+ ```
147
+
148
+ ```go [sol1-Go 模板]
149
+ p := make([]int, n)
150
+ size := make([]int, n)
151
+ for i := range p {
152
+ p[i] = i
153
+ size[i] = 1
154
+ }
155
+
156
+ func find(x int) int {
157
+ if p[x] != x {
158
+ // 路径压缩
159
+ p[x] = find(p[x])
160
+ }
114
161
return p[x]
162
+ }
115
163
116
- # 合并a和b所在的两个集合
117
- p[find(a)] = find(b)
118
- d[find(a)] = distance
164
+ func union(a, b int) {
165
+ pa, pb := find(a), find(b)
166
+ if pa == pb {
167
+ return
168
+ }
169
+ p[pa] = pb
170
+ size[pb] += size[pa]
171
+ }
119
172
```
120
173
121
174
<!-- tabs:start -->
@@ -134,19 +187,14 @@ class Solution:
134
187
135
188
p = list (range (n))
136
189
edgeList.sort(key = lambda x : x[2 ])
137
-
138
- m = len (queries)
139
- indexes = list (range (m))
140
- indexes.sort(key = lambda i : queries[i][2 ])
141
- ans = [False ] * m
142
- i = 0
143
- for j in indexes:
144
- pj, qj, limit = queries[j]
145
- while i < len (edgeList) and edgeList[i][2 ] < limit:
146
- u, v, _ = edgeList[i]
190
+ j = 0
191
+ ans = [False ] * len (queries)
192
+ for i, (a, b, limit) in sorted (enumerate (queries), key = lambda x : x[1 ][2 ]):
193
+ while j < len (edgeList) and edgeList[j][2 ] < limit:
194
+ u, v, _ = edgeList[j]
147
195
p[find(u)] = find(v)
148
- i += 1
149
- ans[j ] = find(pj ) == find(qj )
196
+ j += 1
197
+ ans[i ] = find(a ) == find(b )
150
198
return ans
151
199
```
152
200
@@ -163,23 +211,23 @@ class Solution {
163
211
for (int i = 0 ; i < n; ++ i) {
164
212
p[i] = i;
165
213
}
214
+ Arrays . sort(edgeList, (a, b) - > a[2 ] - b[2 ]);
166
215
int m = queries. length;
167
- Integer [] indexes = new Integer [m];
216
+ boolean [] ans = new boolean [m];
217
+ Integer [] qid = new Integer [m];
168
218
for (int i = 0 ; i < m; ++ i) {
169
- indexes [i] = i;
219
+ qid [i] = i;
170
220
}
171
- Arrays . sort(indexes, Comparator . comparingInt(i - > queries[i][2 ]));
172
- Arrays . sort(edgeList, Comparator . comparingInt(a - > a[2 ]));
173
- boolean [] ans = new boolean [m];
174
- int i = 0 ;
175
- for (int j : indexes) {
176
- int pj = queries[j][0 ], qj = queries[j][1 ], limit = queries[j][2 ];
177
- while (i < edgeList. length && edgeList[i][2 ] < limit) {
178
- int u = edgeList[i][0 ], v = edgeList[i][1 ];
221
+ Arrays . sort(qid, (i, j) - > queries[i][2 ] - queries[j][2 ]);
222
+ int j = 0 ;
223
+ for (int i : qid) {
224
+ int a = queries[i][0 ], b = queries[i][1 ], limit = queries[i][2 ];
225
+ while (j < edgeList. length && edgeList[j][2 ] < limit) {
226
+ int u = edgeList[j][0 ], v = edgeList[j][1 ];
179
227
p[find(u)] = find(v);
180
- ++ i ;
228
+ ++ j ;
181
229
}
182
- ans[j ] = find(pj ) == find(qj );
230
+ ans[i ] = find(a ) == find(b );
183
231
}
184
232
return ans;
185
233
}
@@ -198,39 +246,31 @@ class Solution {
198
246
``` cpp
199
247
class Solution {
200
248
public:
201
- vector<int > p;
202
-
203
249
vector<bool > distanceLimitedPathsExist(int n, vector<vector<int >>& edgeList, vector<vector<int >>& queries) {
204
- p.resize(n);
205
- for (int i = 0; i < n; ++i) p[i] = i;
206
- sort(edgeList.begin(), edgeList.end(), [](const auto& e1, const auto& e2) {
207
- return e1[2] < e2[2];
208
- });
250
+ vector<int > p(n);
251
+ iota(p.begin(), p.end(), 0);
252
+ sort(edgeList.begin(), edgeList.end(), [ ] (auto& a, auto& b) { return a[ 2] < b[ 2] ; });
253
+ function<int(int)> find = [ &] (int x) -> int {
254
+ if (p[ x] != x) p[ x] = find(p[ x] );
255
+ return p[ x] ;
256
+ };
209
257
int m = queries.size();
210
- vector<int> indexes(m);
211
- for (int i = 0; i < m; ++i) indexes[i] = i;
212
- sort(indexes.begin(), indexes.end(), [&](int i, int j) {
213
- return queries[i][2] < queries[j][2];
214
- });
215
-
216
- vector<bool> ans(m, false);
217
- int i = 0;
218
- for (int j : indexes) {
219
- int pj = queries[j][0], qj = queries[j][1], limit = queries[j][2];
220
- while (i < edgeList.size() && edgeList[i][2] < limit) {
221
- int u = edgeList[i][0], v = edgeList[i][1];
258
+ vector<bool > ans(m);
259
+ vector<int > qid(m);
260
+ iota(qid.begin(), qid.end(), 0);
261
+ sort(qid.begin(), qid.end(), [ &] (int i, int j) { return queries[ i] [ 2 ] < queries[ j] [ 2 ] ; });
262
+ int j = 0;
263
+ for (int i : qid) {
264
+ int a = queries[ i] [ 0 ] , b = queries[ i] [ 1 ] , limit = queries[ i] [ 2 ] ;
265
+ while (j < edgeList.size() && edgeList[ j] [ 2 ] < limit) {
266
+ int u = edgeList[ j] [ 0 ] , v = edgeList[ j] [ 1 ] ;
222
267
p[ find(u)] = find(v);
223
- ++i ;
268
+ ++j ;
224
269
}
225
- ans[j ] = find(pj ) == find(qj );
270
+ ans[ i ] = find(a ) == find(b );
226
271
}
227
272
return ans;
228
273
}
229
-
230
- int find (int x) {
231
- if (p[ x] != x) p[ x] = find(p[ x] );
232
- return p[ x] ;
233
- }
234
274
};
235
275
```
236
276
@@ -239,37 +279,33 @@ public:
239
279
```go
240
280
func distanceLimitedPathsExist(n int, edgeList [][]int, queries [][]int) []bool {
241
281
p := make([]int, n)
242
- for i := 0; i < n; i++ {
282
+ for i := range p {
243
283
p[i] = i
244
284
}
245
- var find func(x int) int
285
+ sort.Slice(edgeList, func(i, j int) bool { return edgeList[i][2] < edgeList[j][2] })
286
+ var find func(int) int
246
287
find = func(x int) int {
247
288
if p[x] != x {
248
289
p[x] = find(p[x])
249
290
}
250
291
return p[x]
251
292
}
252
- sort.Slice(edgeList, func(i, j int) bool {
253
- return edgeList[i][2] < edgeList[j][2]
254
- })
255
293
m := len(queries)
256
- indexes := make([]int, m)
257
- for i := 0; i < m; i++ {
258
- indexes[i] = i
259
- }
260
- sort.Slice(indexes, func(i, j int) bool {
261
- return queries[indexes[i]][2] < queries[indexes[j]][2]
262
- })
294
+ qid := make([]int, m)
263
295
ans := make([]bool, m)
264
- i := 0
265
- for _, j := range indexes {
266
- pj, qj, limit := queries[j][0], queries[j][1], queries[j][2]
267
- for i < len(edgeList) && edgeList[i][2] < limit {
268
- u, v := edgeList[i][0], edgeList[i][1]
296
+ for i := range qid {
297
+ qid[i] = i
298
+ }
299
+ sort.Slice(qid, func(i, j int) bool { return queries[qid[i]][2] < queries[qid[j]][2] })
300
+ j := 0
301
+ for _, i := range qid {
302
+ a, b, limit := queries[i][0], queries[i][1], queries[i][2]
303
+ for j < len(edgeList) && edgeList[j][2] < limit {
304
+ u, v := edgeList[j][0], edgeList[j][1]
269
305
p[find(u)] = find(v)
270
- i ++
306
+ j ++
271
307
}
272
- ans[j ] = find(pj ) == find(qj )
308
+ ans[i ] = find(a ) == find(b )
273
309
}
274
310
return ans
275
311
}
0 commit comments