Skip to content

Commit 9971177

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 0521ad3 commit 9971177

File tree

2 files changed

+82
-97
lines changed

2 files changed

+82
-97
lines changed

content/6_Advanced/Offline_Del.mdx

+59-68
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ deleting from a data structure while only using insertion operations.
1919

2020
<Warning title="Watch Out!">
2121

22-
Because path compression is amortized, it does not guarantee
22+
Because path compression is amortized, it does not guarantee
2323
$\mathcal{O}(N \log^2 N)$ runtime. You _must_ use merging by rank. An explanation is given [here](https://codeforces.com/blog/entry/90340?#comment-787571).
2424

2525
</Warning>
2626

2727
### Implementation
2828

29-
Adding on to usual DSU, we can store the parent
29+
Adding on to usual DSU, we can store the parent
3030
and sizes of nodes that are being merged before each merge. This allows us revert each node to their parents before the merge so that the rollback function can use the information to undo the merges.
3131

3232
We can store each state of the DSU using an integer, captured by the snapshot function which returns the number of old merges that has not been rolled back. It's similar taking a picture of something, and years later going back into your photo album and scrolling up until you find this picture.
@@ -40,7 +40,7 @@ struct DSU {
4040
vector<int> p, sz;
4141

4242
// stores previous unites
43-
vector<pair<int&, int>> history;
43+
vector<pair<int &, int>> history;
4444

4545
DSU(int n) {
4646
p.resize(n);
@@ -53,13 +53,13 @@ struct DSU {
5353
void unite(int a, int b) {
5454
a = get(a);
5555
b = get(b);
56-
if(a == b) return;
56+
if (a == b) return;
5757
if (sz[a] < sz[b]) { swap(a, b); }
58-
58+
5959
// save this unite operation
6060
history.push_back({sz[a], sz[a]});
6161
history.push_back({p[b], p[b]});
62-
62+
6363
p[b] = a;
6464
sz[a] += sz[b];
6565
}
@@ -104,11 +104,11 @@ These types of problems involve determining whether pairs of nodes are in the sa
104104

105105
<FocusProblem problem="sam" />
106106

107-
## Solution - Vertex Add Component Sum
107+
## Solution - Vertex Add Component Sum
108108

109-
For dynamic connectivity problems, we say for every query, there's an interval $[l, r]$ where it's active. Obviously, for each edge add/remove query, $l = $ (the index of the query which adds the edge), and $r = $ (the index of the query which removes the edge) $-1$. If an edge never gets removed, then $r = q - 1$. Note that we assign intervals such that for queries outside the interval, they are completely unaffected by this query. We can use similar reasoning to construct intervals for type $2$ and $3$ queries.
109+
For dynamic connectivity problems, we say for every query, there's an interval $[l, r]$ where it's active. Obviously, for each edge add/remove query, $l = $ (the index of the query which adds the edge), and $r = $ (the index of the query which removes the edge) $-1$. If an edge never gets removed, then $r = q - 1$. Note that we assign intervals such that for queries outside the interval, they are completely unaffected by this query. We can use similar reasoning to construct intervals for type $2$ and $3$ queries.
110110

111-
We can now construct a query tree. If our interval is encapsulated by the the tree's interval, then we can add our query to the node corresponding to the interval. When answering queries, as we enter the interval, we can process all the operations inside the interval. As we exit the interval, we need to undo them. If we are at a leaf, we can answer type $3$ queries since we have processed all queries outside this interval $[i, i]$. Since we are processing intevals by halves each time, the depth is at most $\log n$, similar to divide and conquer.
111+
We can now construct a query tree. If our interval is encapsulated by the the tree's interval, then we can add our query to the node corresponding to the interval. When answering queries, as we enter the interval, we can process all the operations inside the interval. As we exit the interval, we need to undo them. If we are at a leaf, we can answer type $3$ queries since we have processed all queries outside this interval $[i, i]$. Since we are processing intevals by halves each time, the depth is at most $\log n$, similar to divide and conquer.
112112

113113
See the code below for implementation details. Note that similar to unite operations, we can also perform and undo operations of type $2$!
114114

@@ -123,10 +123,10 @@ using ll = long long;
123123
124124
struct DSU {
125125
vector<ll> p, sz, sum;
126-
126+
127127
// stores all history info related to merges
128-
vector<pair<ll&, ll>> history;
129-
128+
vector<pair<ll &, ll>> history;
129+
130130
DSU(int n) {
131131
p.resize(n);
132132
sz.resize(n, 1);
@@ -139,9 +139,9 @@ struct DSU {
139139
void unite(int a, int b) {
140140
a = get(a);
141141
b = get(b);
142-
if(a == b) return;
142+
if (a == b) return;
143143
if (sz[a] < sz[b]) { swap(a, b); }
144-
144+
145145
// add to history
146146
history.push_back({p[b], p[b]});
147147
history.push_back({sz[a], sz[a]});
@@ -151,19 +151,17 @@ struct DSU {
151151
sz[a] += sz[b];
152152
sum[a] += sum[b];
153153
}
154-
155-
void add(int x, ll v){
154+
155+
void add(int x, ll v) {
156156
x = get(x);
157157
history.push_back({sum[x], sum[x]});
158158
sum[x] += v;
159159
}
160-
161-
int snapshot(){
162-
return history.size();
163-
}
160+
161+
int snapshot() { return history.size(); }
164162
165163
void rollback(int until) {
166-
while(snapshot() > until){
164+
while (snapshot() > until) {
167165
history.back().first = history.back().second;
168166
history.pop_back();
169167
}
@@ -174,15 +172,15 @@ const int MAXN = 3e5 + 5;
174172
175173
DSU dsu(MAXN);
176174
177-
struct query{
175+
struct query {
178176
int t, u, v, x;
179177
};
180178
181179
vector<query> tree[MAXN * 4];
182180
183-
void update(query& q, int v, int l, int r, int L, int R){
184-
if(l > R || r < L) return;
185-
if(l <= L && r >= R){
181+
void update(query &q, int v, int l, int r, int L, int R) {
182+
if (l > R || r < L) return;
183+
if (l <= L && r >= R) {
186184
tree[v].push_back(q);
187185
return;
188186
}
@@ -191,27 +189,19 @@ void update(query& q, int v, int l, int r, int L, int R){
191189
update(q, v * 2 + 1, l, r, m + 1, R);
192190
}
193191
194-
195-
void dfs(int v, int l, int r, vector<ll>& ans){
192+
void dfs(int v, int l, int r, vector<ll> &ans) {
196193
int snapshot = dsu.snapshot();
197194
// perform all available operations upon entering
198-
for(query& q: tree[v]){
199-
if(q.t == 1){
200-
dsu.unite(q.u, q.v);
201-
}
202-
if(q.t == 2){
203-
dsu.add(q.v, q.x);
204-
}
205-
}
206-
if(l == r){
195+
for (query &q : tree[v]) {
196+
if (q.t == 1) { dsu.unite(q.u, q.v); }
197+
if (q.t == 2) { dsu.add(q.v, q.x); }
198+
}
199+
if (l == r) {
207200
// answer type 3 query if we have one
208-
for(query& q: tree[v]){
209-
if(q.t == 3){
210-
ans[l] = dsu.sum[dsu.get(q.v)];
211-
}
201+
for (query &q : tree[v]) {
202+
if (q.t == 3) { ans[l] = dsu.sum[dsu.get(q.v)]; }
212203
}
213-
}
214-
else{
204+
} else {
215205
// go deeper into the tree
216206
int m = (l + r) / 2;
217207
dfs(2 * v, l, m, ans);
@@ -222,58 +212,59 @@ void dfs(int v, int l, int r, vector<ll>& ans){
222212
}
223213
224214
int main() {
225-
cin.tie(0) -> sync_with_stdio(0);
226-
int n, q; cin >> n >> q;
227-
for(int i = 0; i < n; i++){
228-
ll x; cin >> x;
215+
cin.tie(0)->sync_with_stdio(0);
216+
int n, q;
217+
cin >> n >> q;
218+
for (int i = 0; i < n; i++) {
219+
ll x;
220+
cin >> x;
229221
dsu.sum[i] = x;
230222
}
231223
map<pair<int, int>, int> index_added;
232-
for(int i = 0; i < q; i++){
233-
int t; cin >> t;
234-
if(t == 0){
235-
int u, v; cin >> u >> v;
236-
if(u > v) swap(u, v);
224+
for (int i = 0; i < q; i++) {
225+
int t;
226+
cin >> t;
227+
if (t == 0) {
228+
int u, v;
229+
cin >> u >> v;
230+
if (u > v) swap(u, v);
237231
// store index this edge is added, marks beginning of interval
238232
index_added[{u, v}] = i;
239-
}
240-
else if(t == 1){
241-
int u, v; cin >> u >> v;
242-
if(u > v) swap(u, v);
233+
} else if (t == 1) {
234+
int u, v;
235+
cin >> u >> v;
236+
if (u > v) swap(u, v);
243237
query cur_q = {1, u, v};
244238
// add all edges that are deleted to interval [index added, i - 1]
245239
update(cur_q, 1, index_added[{u, v}], i - 1, 0, q - 1);
246240
index_added[{u, v}] = -1;
247-
}
248-
else if(t == 2){
249-
int v, x; cin >> v >> x;
241+
} else if (t == 2) {
242+
int v, x;
243+
cin >> v >> x;
250244
query cur_q = {2, -1, v, x};
251245
// add all sum queries to interval [i, q - 1]
252246
update(cur_q, 1, i, q - 1, 0, q - 1);
253-
}
254-
else{
255-
int v; cin >> v;
247+
} else {
248+
int v;
249+
cin >> v;
256250
query cur_q = {3, -1, v};
257251
// add all output queries to interval [i, i]
258252
update(cur_q, 1, i, i, 0, q - 1);
259253
}
260254
}
261255
// add all edges that are not deleted to interval [index added, q - 1]
262-
for(auto [edge, index]: index_added){
263-
if(index != -1){
256+
for (auto [edge, index] : index_added) {
257+
if (index != -1) {
264258
query cur_q = {1, edge.first, edge.second};
265259
update(cur_q, 1, index, q - 1, 0, q - 1);
266260
}
267261
}
268262
vector<ll> ans(q, -1);
269263
dfs(1, 0, q - 1, ans);
270-
for(int i = 0; i < q; i++){
271-
if(ans[i] != -1){
272-
cout << ans[i] << "\n";
273-
}
264+
for (int i = 0; i < q; i++) {
265+
if (ans[i] != -1) { cout << ans[i] << "\n"; }
274266
}
275267
}
276-
277268
```
278269

279270
</CPPSection>

solutions/advanced/mmcc-inaho.mdx

+23-29
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
id: mmcc-inaho
3-
source: MMCC
3+
source: MMCC
44
title: Inaho
55
author: Chongtian Ma
66
---
@@ -13,20 +13,22 @@ To easily test your code from samples, copy and paste the following main functio
1313
<CPPSection>
1414

1515
```cpp
16-
int main(){
17-
int n, q; cin >> n >> q;
16+
int main() {
17+
int n, q;
18+
cin >> n >> q;
1819
Init(n);
19-
while(q--){
20-
int t; cin >> t;
21-
if(t == 1){
22-
int u, v; cin >> u >> v;
20+
while (q--) {
21+
int t;
22+
cin >> t;
23+
if (t == 1) {
24+
int u, v;
25+
cin >> u >> v;
2326
AddEdge(u, v);
2427
}
25-
if(t == 2){
26-
RemoveLastEdge();
27-
}
28-
if(t == 3){
29-
int u; cin >> u;
28+
if (t == 2) { RemoveLastEdge(); }
29+
if (t == 3) {
30+
int u;
31+
cin >> u;
3032
cout << GetSize(u) << endl;
3133
}
3234
}
@@ -61,7 +63,7 @@ Expected Output:
6163
</LanguageSection>
6264
</Info>
6365
64-
## Implementation
66+
## Implementation
6567
6668
This is a standard test of your DSU with rollback template.
6769
@@ -75,10 +77,10 @@ using namespace std;
7577

7678
struct DSU {
7779
vector<int> p, sz;
78-
80+
7981
// stores history
80-
vector<pair<int&, int>> psnap, szsnap;
81-
82+
vector<pair<int &, int>> psnap, szsnap;
83+
8284
DSU(int n) {
8385
p.resize(n);
8486
sz.resize(n, 1);
@@ -91,7 +93,7 @@ struct DSU {
9193
a = get(a);
9294
b = get(b);
9395
if (sz[a] < sz[b]) { swap(a, b); }
94-
96+
9597
// add to history
9698
szsnap.push_back({sz[a], sz[a]});
9799
psnap.push_back({p[b], p[b]});
@@ -113,21 +115,13 @@ struct DSU {
113115
const int MAXN = 500000;
114116
DSU dsu(MAXN);
115117

116-
void Init(int n){
117-
118-
}
118+
void Init(int n) {}
119119

120-
void AddEdge(int u, int v){
121-
dsu.unite(--u, --v);
122-
}
120+
void AddEdge(int u, int v) { dsu.unite(--u, --v); }
123121

124-
void RemoveLastEdge(){
125-
dsu.rollback();
126-
}
122+
void RemoveLastEdge() { dsu.rollback(); }
127123

128-
int GetSize(int u){
129-
return dsu.sz[dsu.get(--u)];
130-
}
124+
int GetSize(int u) { return dsu.sz[dsu.get(--u)]; }
131125
```
132126

133127
</CPPSection>

0 commit comments

Comments
 (0)