From 0160020bf7c8d5198fb189d2ee74eb10c6933157 Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Fri, 3 May 2024 16:11:16 -0700 Subject: [PATCH 01/23] wrote the stuff --- content/5_Plat/Binary_Jump.problems.json | 3 +- solutions/platinum/bts-hotcold.mdx | 189 +++++++++++++++++++++++ 2 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 solutions/platinum/bts-hotcold.mdx diff --git a/content/5_Plat/Binary_Jump.problems.json b/content/5_Plat/Binary_Jump.problems.json index 34041d5588..ecce628300 100644 --- a/content/5_Plat/Binary_Jump.problems.json +++ b/content/5_Plat/Binary_Jump.problems.json @@ -347,8 +347,7 @@ "isStarred": false, "tags": ["LCA"], "solutionMetadata": { - "kind": "autogen-label-from-site", - "site": "DMOJ" + "kind": "internal" } }, { diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx new file mode 100644 index 0000000000..cdb4f84fd3 --- /dev/null +++ b/solutions/platinum/bts-hotcold.mdx @@ -0,0 +1,189 @@ +--- +id: bts-hotcold +source: Back to School 2017 +title: Hot & Cold +author: Justin Ji +--- + +## Explanation + +Let $a$, $b$, and $t$ be the nodes given in each query, and $p$ be +the LCA of $a$ and $b$. Directly updating the path from $a$ +to $b$ is difficult to do, so breaking up the path into smaller segments +is necessary. To do that, we use some casework. + +**Case 1: $p$ is equal to $t$, or $t$ is not inside $p$'s subtree.** + +In this case, we just update the path from $a$ to $p$ and +$b$ to $p$. Note that if $p$ is equal to either $a$ or $b$, +then this case must be handled slightly differently. + +**Case 2: $p$ is equal to either $a$ or $b$.** + +Let the first ancestor of $t$ that is on the path from $a$ to $b$ +be $l$. WLOG, let $b$ be equal to $p$. Then, we update the path from +$a$ to $l$ and the path from $l$ to $b$. + +**Case 3: The LCA of $t$ with $a$ and $b$ is the same as $p$.** + +Handle this case similarly to case 1. + +**Case 4: Either the LCA of $t$ and $a$ or the LCA of $t$ and $b$ lie on the path from $a$ to $b$.** + +In this case, split the path updates into 3 segments. Let the lower of the two LCAs mentioned +be $l$, and the node which is an ancestor of $l$ be $a$. Then, just update the path from +$a$ to $l$, $l$ to $p$, and the path from $b$ to $p$. + +**Handling Path Updates** + +Note that based on how we did our casework on the paths, that all path updates are between a given +node and its ancestor. Also, note that the path updates are just adding distances which either increase +or decrease by a fixed amount every time. So, we can sort of do a difference array on a tree, where we DFS +down the tree and apply the differences while walking back up the tree. + +**Time Complexity:** $\mathcal{O}(N\log{N})$ + + + + + +```cpp +#include +using namespace std; +using ll = long long; + +int main() { + int n, s; + cin >> n >> s; + // 1 indexing makes updates easier + vector> adj(n + 1); + for (int i = 1; i < n; i++) { + int u, v; + cin >> u >> v; + adj[u].push_back(v); + adj[v].push_back(u); + } + + // perform Euler tour and binary lifts + const int LOG = 1 + (int) log2(n); + vector> lift(LOG, vector(n + 1)); + vector tin(n + 1); + vector tout(n + 1); + vector depth(n + 1); + int timer = 0; + function tour = [&](int u, int p) { + tin[u] = timer++; + lift[0][u] = p; + depth[u] = depth[p] + 1; + for (int i = 1; i < LOG; i++) { + lift[i][u] = lift[i - 1][lift[i - 1][u]]; + } + for (int v : adj[u]) { + if (v != p) { tour(v, u); } + } + tout[u] = timer - 1; + }; + tour(1, 0); + tin[0] = -1, tout[0] = n + 1; // so LCA works + + // just some helper methods to simplify the code + auto isAnc = [&](int u, int v) -> bool { + return tin[u] <= tin[v] && tout[v] <= tout[u]; + }; + auto lca = [&](int u, int v) -> int { + if (isAnc(u, v)) return u; + if (isAnc(v, u)) return v; + for (int i = LOG - 1; i >= 0; i--) { + if (!isAnc(lift[i][u], v)) { + u = lift[i][u]; + } + } + return lift[0][u]; + }; + auto dist = [&](int a, int b, int anc = -1) -> int { + if (anc == -1) { anc = lca(a, b); } + return depth[a] + depth[b] - 2 * depth[anc]; + }; + + // calculating difference array updates + vector> diff(n + 1, {0, 0}); + auto upd = [&](int u, int v, int initial, int change) -> void { + /* + * Precondition: v is an ancestor of u, or equal to u. + * + * Updates path from u to v, excluding v. When walking up from + * u to v, the added initial value is updated with the "change". + */ + diff[u][0] += initial; + diff[u][1] += change; + if (change > 0) { + diff[v][0] -= initial + (depth[u] - depth[v]); + } else { + diff[v][0] -= initial - (depth[u] - depth[v]); + } + diff[v][1] -= change; + }; + auto qry = [&](int a, int b, int t) -> void { + int anc = lca(a, b); + if (anc == t || !isAnc(anc, t)) { + // t is either the LCA, or not inside the subtree of the LCA + if (anc == a || anc == b) { + if (anc == a) { swap(a, b); } + upd(a, lift[0][b], dist(a, t), -1); + } else { + upd(a, anc, dist(a, t), -1); + upd(b, lift[0][anc], dist(b, t), -1); + } + } else { + // split_1 and split_2 are candidates for the locations where + // the path updates go from increasing to decreasing (or vice versa) + int split_1 = lca(a, t); + int split_2 = lca(b, t); + if (anc == a || anc == b) { + // path from a to be is just a walk upward + if (anc == a) { + swap(a, b); + swap(split_1, split_2); + } + upd(a, split_1, dist(a, t, split_1), -1); + upd(split_1, lift[0][b], dist(t, split_1, split_1), 1); + } else if (split_1 == anc && split_2 == anc) { + // lca(a, t) and lca(b, t) are both lca(a, b) + upd(a, anc, dist(a, t, anc), -1); + upd(b, lift[0][anc], dist(b, t, anc), -1); + } else { + // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) + if (depth[split_1] < depth[split_2]) { + swap(split_1, split_2), swap(a, b); + } + upd(a, split_1, dist(a, t, split_1), -1); + upd(split_1, anc, dist(split_1, t, split_1), 1); + upd(b, lift[0][anc], dist(b, t, anc), -1); + } + } + }; + while (s--) { + int a, b, t; + cin >> a >> b >> t; + qry(a, b, t); + } + + // calculate the final result using the difference arrays + function dfs = [&](int u, int p) { + for (int v : adj[u]) { + if (v == p) { continue; } + dfs(v, u); + diff[u][0] += diff[v][0] + diff[v][1]; + diff[u][1] += diff[v][1]; + } + }; + dfs(1, 0); + for (int i = 1; i <= n; i++) { + cout << diff[i][0] << " \n"[i == n]; + } +} +``` + + + + \ No newline at end of file From 248753ee18f88acaec8c8ae5f33fd6e8294ded44 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 23:16:04 +0000 Subject: [PATCH 02/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/bts-hotcold.mdx | 268 ++++++++++++++--------------- 1 file changed, 132 insertions(+), 136 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index cdb4f84fd3..b7e236bb4e 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -7,21 +7,21 @@ author: Justin Ji ## Explanation -Let $a$, $b$, and $t$ be the nodes given in each query, and $p$ be -the LCA of $a$ and $b$. Directly updating the path from $a$ +Let $a$, $b$, and $t$ be the nodes given in each query, and $p$ be +the LCA of $a$ and $b$. Directly updating the path from $a$ to $b$ is difficult to do, so breaking up the path into smaller segments is necessary. To do that, we use some casework. **Case 1: $p$ is equal to $t$, or $t$ is not inside $p$'s subtree.** -In this case, we just update the path from $a$ to $p$ and +In this case, we just update the path from $a$ to $p$ and $b$ to $p$. Note that if $p$ is equal to either $a$ or $b$, then this case must be handled slightly differently. **Case 2: $p$ is equal to either $a$ or $b$.** Let the first ancestor of $t$ that is on the path from $a$ to $b$ -be $l$. WLOG, let $b$ be equal to $p$. Then, we update the path from +be $l$. WLOG, let $b$ be equal to $p$. Then, we update the path from $a$ to $l$ and the path from $l$ to $b$. **Case 3: The LCA of $t$ with $a$ and $b$ is the same as $p$.** @@ -31,14 +31,14 @@ Handle this case similarly to case 1. **Case 4: Either the LCA of $t$ and $a$ or the LCA of $t$ and $b$ lie on the path from $a$ to $b$.** In this case, split the path updates into 3 segments. Let the lower of the two LCAs mentioned -be $l$, and the node which is an ancestor of $l$ be $a$. Then, just update the path from +be $l$, and the node which is an ancestor of $l$ be $a$. Then, just update the path from $a$ to $l$, $l$ to $p$, and the path from $b$ to $p$. **Handling Path Updates** Note that based on how we did our casework on the paths, that all path updates are between a given -node and its ancestor. Also, note that the path updates are just adding distances which either increase -or decrease by a fixed amount every time. So, we can sort of do a difference array on a tree, where we DFS +node and its ancestor. Also, note that the path updates are just adding distances which either increase +or decrease by a fixed amount every time. So, we can sort of do a difference array on a tree, where we DFS down the tree and apply the differences while walking back up the tree. **Time Complexity:** $\mathcal{O}(N\log{N})$ @@ -53,137 +53,133 @@ using namespace std; using ll = long long; int main() { - int n, s; - cin >> n >> s; - // 1 indexing makes updates easier - vector> adj(n + 1); - for (int i = 1; i < n; i++) { - int u, v; - cin >> u >> v; - adj[u].push_back(v); - adj[v].push_back(u); - } - - // perform Euler tour and binary lifts - const int LOG = 1 + (int) log2(n); - vector> lift(LOG, vector(n + 1)); - vector tin(n + 1); - vector tout(n + 1); - vector depth(n + 1); - int timer = 0; - function tour = [&](int u, int p) { - tin[u] = timer++; - lift[0][u] = p; - depth[u] = depth[p] + 1; - for (int i = 1; i < LOG; i++) { - lift[i][u] = lift[i - 1][lift[i - 1][u]]; - } - for (int v : adj[u]) { - if (v != p) { tour(v, u); } - } - tout[u] = timer - 1; - }; - tour(1, 0); - tin[0] = -1, tout[0] = n + 1; // so LCA works - - // just some helper methods to simplify the code - auto isAnc = [&](int u, int v) -> bool { - return tin[u] <= tin[v] && tout[v] <= tout[u]; - }; - auto lca = [&](int u, int v) -> int { - if (isAnc(u, v)) return u; - if (isAnc(v, u)) return v; - for (int i = LOG - 1; i >= 0; i--) { - if (!isAnc(lift[i][u], v)) { - u = lift[i][u]; - } - } - return lift[0][u]; - }; - auto dist = [&](int a, int b, int anc = -1) -> int { - if (anc == -1) { anc = lca(a, b); } - return depth[a] + depth[b] - 2 * depth[anc]; - }; - - // calculating difference array updates - vector> diff(n + 1, {0, 0}); - auto upd = [&](int u, int v, int initial, int change) -> void { - /* - * Precondition: v is an ancestor of u, or equal to u. - * - * Updates path from u to v, excluding v. When walking up from - * u to v, the added initial value is updated with the "change". - */ - diff[u][0] += initial; - diff[u][1] += change; - if (change > 0) { - diff[v][0] -= initial + (depth[u] - depth[v]); - } else { - diff[v][0] -= initial - (depth[u] - depth[v]); - } - diff[v][1] -= change; - }; - auto qry = [&](int a, int b, int t) -> void { - int anc = lca(a, b); - if (anc == t || !isAnc(anc, t)) { - // t is either the LCA, or not inside the subtree of the LCA - if (anc == a || anc == b) { - if (anc == a) { swap(a, b); } - upd(a, lift[0][b], dist(a, t), -1); - } else { - upd(a, anc, dist(a, t), -1); - upd(b, lift[0][anc], dist(b, t), -1); - } - } else { - // split_1 and split_2 are candidates for the locations where - // the path updates go from increasing to decreasing (or vice versa) - int split_1 = lca(a, t); - int split_2 = lca(b, t); - if (anc == a || anc == b) { - // path from a to be is just a walk upward - if (anc == a) { - swap(a, b); - swap(split_1, split_2); - } - upd(a, split_1, dist(a, t, split_1), -1); - upd(split_1, lift[0][b], dist(t, split_1, split_1), 1); - } else if (split_1 == anc && split_2 == anc) { - // lca(a, t) and lca(b, t) are both lca(a, b) - upd(a, anc, dist(a, t, anc), -1); - upd(b, lift[0][anc], dist(b, t, anc), -1); - } else { - // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) - if (depth[split_1] < depth[split_2]) { - swap(split_1, split_2), swap(a, b); - } - upd(a, split_1, dist(a, t, split_1), -1); - upd(split_1, anc, dist(split_1, t, split_1), 1); - upd(b, lift[0][anc], dist(b, t, anc), -1); - } - } - }; - while (s--) { - int a, b, t; - cin >> a >> b >> t; - qry(a, b, t); - } - - // calculate the final result using the difference arrays - function dfs = [&](int u, int p) { - for (int v : adj[u]) { - if (v == p) { continue; } - dfs(v, u); - diff[u][0] += diff[v][0] + diff[v][1]; - diff[u][1] += diff[v][1]; - } - }; - dfs(1, 0); - for (int i = 1; i <= n; i++) { - cout << diff[i][0] << " \n"[i == n]; - } + int n, s; + cin >> n >> s; + // 1 indexing makes updates easier + vector> adj(n + 1); + for (int i = 1; i < n; i++) { + int u, v; + cin >> u >> v; + adj[u].push_back(v); + adj[v].push_back(u); + } + + // perform Euler tour and binary lifts + const int LOG = 1 + (int)log2(n); + vector> lift(LOG, vector(n + 1)); + vector tin(n + 1); + vector tout(n + 1); + vector depth(n + 1); + int timer = 0; + function tour = [&](int u, int p) { + tin[u] = timer++; + lift[0][u] = p; + depth[u] = depth[p] + 1; + for (int i = 1; i < LOG; i++) { + lift[i][u] = lift[i - 1][lift[i - 1][u]]; + } + for (int v : adj[u]) { + if (v != p) { tour(v, u); } + } + tout[u] = timer - 1; + }; + tour(1, 0); + tin[0] = -1, tout[0] = n + 1; // so LCA works + + // just some helper methods to simplify the code + auto isAnc = [&](int u, int v) -> bool { + return tin[u] <= tin[v] && tout[v] <= tout[u]; + }; + auto lca = [&](int u, int v) -> int { + if (isAnc(u, v)) return u; + if (isAnc(v, u)) return v; + for (int i = LOG - 1; i >= 0; i--) { + if (!isAnc(lift[i][u], v)) { u = lift[i][u]; } + } + return lift[0][u]; + }; + auto dist = [&](int a, int b, int anc = -1) -> int { + if (anc == -1) { anc = lca(a, b); } + return depth[a] + depth[b] - 2 * depth[anc]; + }; + + // calculating difference array updates + vector> diff(n + 1, {0, 0}); + auto upd = [&](int u, int v, int initial, int change) -> void { + /* + * Precondition: v is an ancestor of u, or equal to u. + * + * Updates path from u to v, excluding v. When walking up from + * u to v, the added initial value is updated with the "change". + */ + diff[u][0] += initial; + diff[u][1] += change; + if (change > 0) { + diff[v][0] -= initial + (depth[u] - depth[v]); + } else { + diff[v][0] -= initial - (depth[u] - depth[v]); + } + diff[v][1] -= change; + }; + auto qry = [&](int a, int b, int t) -> void { + int anc = lca(a, b); + if (anc == t || !isAnc(anc, t)) { + // t is either the LCA, or not inside the subtree of the LCA + if (anc == a || anc == b) { + if (anc == a) { swap(a, b); } + upd(a, lift[0][b], dist(a, t), -1); + } else { + upd(a, anc, dist(a, t), -1); + upd(b, lift[0][anc], dist(b, t), -1); + } + } else { + // split_1 and split_2 are candidates for the locations where + // the path updates go from increasing to decreasing (or vice versa) + int split_1 = lca(a, t); + int split_2 = lca(b, t); + if (anc == a || anc == b) { + // path from a to be is just a walk upward + if (anc == a) { + swap(a, b); + swap(split_1, split_2); + } + upd(a, split_1, dist(a, t, split_1), -1); + upd(split_1, lift[0][b], dist(t, split_1, split_1), 1); + } else if (split_1 == anc && split_2 == anc) { + // lca(a, t) and lca(b, t) are both lca(a, b) + upd(a, anc, dist(a, t, anc), -1); + upd(b, lift[0][anc], dist(b, t, anc), -1); + } else { + // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) + if (depth[split_1] < depth[split_2]) { + swap(split_1, split_2), swap(a, b); + } + upd(a, split_1, dist(a, t, split_1), -1); + upd(split_1, anc, dist(split_1, t, split_1), 1); + upd(b, lift[0][anc], dist(b, t, anc), -1); + } + } + }; + while (s--) { + int a, b, t; + cin >> a >> b >> t; + qry(a, b, t); + } + + // calculate the final result using the difference arrays + function dfs = [&](int u, int p) { + for (int v : adj[u]) { + if (v == p) { continue; } + dfs(v, u); + diff[u][0] += diff[v][0] + diff[v][1]; + diff[u][1] += diff[v][1]; + } + }; + dfs(1, 0); + for (int i = 1; i <= n; i++) { cout << diff[i][0] << " \n"[i == n]; } } ``` - \ No newline at end of file + From 872a572c92e7f0c16b60b0c87df551dadf299161 Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Fri, 3 May 2024 19:57:34 -0700 Subject: [PATCH 03/23] did some style changes --- solutions/platinum/bts-hotcold.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index cdb4f84fd3..5e957fa2aa 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -87,14 +87,14 @@ int main() { tin[0] = -1, tout[0] = n + 1; // so LCA works // just some helper methods to simplify the code - auto isAnc = [&](int u, int v) -> bool { + auto isAncestor = [&](int u, int v) -> bool { return tin[u] <= tin[v] && tout[v] <= tout[u]; }; auto lca = [&](int u, int v) -> int { - if (isAnc(u, v)) return u; - if (isAnc(v, u)) return v; + if (isAncestor(u, v)) { return u; } + if (isAncestor(v, u)) { return v; } for (int i = LOG - 1; i >= 0; i--) { - if (!isAnc(lift[i][u], v)) { + if (!isAncestor(lift[i][u], v)) { u = lift[i][u]; } } @@ -125,7 +125,7 @@ int main() { }; auto qry = [&](int a, int b, int t) -> void { int anc = lca(a, b); - if (anc == t || !isAnc(anc, t)) { + if (anc == t || !isAncestor(anc, t)) { // t is either the LCA, or not inside the subtree of the LCA if (anc == a || anc == b) { if (anc == a) { swap(a, b); } @@ -162,7 +162,7 @@ int main() { } } }; - while (s--) { + for (int i = 0; i < s; i++) { int a, b, t; cin >> a >> b >> t; qry(a, b, t); From 3a57e62dae9e569d92f8c581b8f9bd2d3d6f9367 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 03:02:18 +0000 Subject: [PATCH 04/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/bts-hotcold.mdx | 186 ++++++++++++++--------------- 1 file changed, 91 insertions(+), 95 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 8cf5a5f419..74a766e960 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -86,101 +86,97 @@ int main() { tour(1, 0); tin[0] = -1, tout[0] = n + 1; // so LCA works - // just some helper methods to simplify the code - auto isAncestor = [&](int u, int v) -> bool { - return tin[u] <= tin[v] && tout[v] <= tout[u]; - }; - auto lca = [&](int u, int v) -> int { - if (isAncestor(u, v)) { return u; } - if (isAncestor(v, u)) { return v; } - for (int i = LOG - 1; i >= 0; i--) { - if (!isAncestor(lift[i][u], v)) { - u = lift[i][u]; - } - } - return lift[0][u]; - }; - auto dist = [&](int a, int b, int anc = -1) -> int { - if (anc == -1) { anc = lca(a, b); } - return depth[a] + depth[b] - 2 * depth[anc]; - }; - - // calculating difference array updates - vector> diff(n + 1, {0, 0}); - auto upd = [&](int u, int v, int initial, int change) -> void { - /* - * Precondition: v is an ancestor of u, or equal to u. - * - * Updates path from u to v, excluding v. When walking up from - * u to v, the added initial value is updated with the "change". - */ - diff[u][0] += initial; - diff[u][1] += change; - if (change > 0) { - diff[v][0] -= initial + (depth[u] - depth[v]); - } else { - diff[v][0] -= initial - (depth[u] - depth[v]); - } - diff[v][1] -= change; - }; - auto qry = [&](int a, int b, int t) -> void { - int anc = lca(a, b); - if (anc == t || !isAncestor(anc, t)) { - // t is either the LCA, or not inside the subtree of the LCA - if (anc == a || anc == b) { - if (anc == a) { swap(a, b); } - upd(a, lift[0][b], dist(a, t), -1); - } else { - upd(a, anc, dist(a, t), -1); - upd(b, lift[0][anc], dist(b, t), -1); - } - } else { - // split_1 and split_2 are candidates for the locations where - // the path updates go from increasing to decreasing (or vice versa) - int split_1 = lca(a, t); - int split_2 = lca(b, t); - if (anc == a || anc == b) { - // path from a to be is just a walk upward - if (anc == a) { - swap(a, b); - swap(split_1, split_2); - } - upd(a, split_1, dist(a, t, split_1), -1); - upd(split_1, lift[0][b], dist(t, split_1, split_1), 1); - } else if (split_1 == anc && split_2 == anc) { - // lca(a, t) and lca(b, t) are both lca(a, b) - upd(a, anc, dist(a, t, anc), -1); - upd(b, lift[0][anc], dist(b, t, anc), -1); - } else { - // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) - if (depth[split_1] < depth[split_2]) { - swap(split_1, split_2), swap(a, b); - } - upd(a, split_1, dist(a, t, split_1), -1); - upd(split_1, anc, dist(split_1, t, split_1), 1); - upd(b, lift[0][anc], dist(b, t, anc), -1); - } - } - }; - for (int i = 0; i < s; i++) { - int a, b, t; - cin >> a >> b >> t; - qry(a, b, t); - } - - // calculate the final result using the difference arrays - function dfs = [&](int u, int p) { - for (int v : adj[u]) { - if (v == p) { continue; } - dfs(v, u); - diff[u][0] += diff[v][0] + diff[v][1]; - diff[u][1] += diff[v][1]; - } - }; - dfs(1, 0); - for (int i = 1; i <= n; i++) { - cout << diff[i][0] << " \n"[i == n]; - } + // just some helper methods to simplify the code + auto isAncestor = [&](int u, int v) -> bool { + return tin[u] <= tin[v] && tout[v] <= tout[u]; + }; + auto lca = [&](int u, int v) -> int { + if (isAncestor(u, v)) { return u; } + if (isAncestor(v, u)) { return v; } + for (int i = LOG - 1; i >= 0; i--) { + if (!isAncestor(lift[i][u], v)) { u = lift[i][u]; } + } + return lift[0][u]; + }; + auto dist = [&](int a, int b, int anc = -1) -> int { + if (anc == -1) { anc = lca(a, b); } + return depth[a] + depth[b] - 2 * depth[anc]; + }; + + // calculating difference array updates + vector> diff(n + 1, {0, 0}); + auto upd = [&](int u, int v, int initial, int change) -> void { + /* + * Precondition: v is an ancestor of u, or equal to u. + * + * Updates path from u to v, excluding v. When walking up from + * u to v, the added initial value is updated with the "change". + */ + diff[u][0] += initial; + diff[u][1] += change; + if (change > 0) { + diff[v][0] -= initial + (depth[u] - depth[v]); + } else { + diff[v][0] -= initial - (depth[u] - depth[v]); + } + diff[v][1] -= change; + }; + auto qry = [&](int a, int b, int t) -> void { + int anc = lca(a, b); + if (anc == t || !isAncestor(anc, t)) { + // t is either the LCA, or not inside the subtree of the LCA + if (anc == a || anc == b) { + if (anc == a) { swap(a, b); } + upd(a, lift[0][b], dist(a, t), -1); + } else { + upd(a, anc, dist(a, t), -1); + upd(b, lift[0][anc], dist(b, t), -1); + } + } else { + // split_1 and split_2 are candidates for the locations where + // the path updates go from increasing to decreasing (or vice versa) + int split_1 = lca(a, t); + int split_2 = lca(b, t); + if (anc == a || anc == b) { + // path from a to be is just a walk upward + if (anc == a) { + swap(a, b); + swap(split_1, split_2); + } + upd(a, split_1, dist(a, t, split_1), -1); + upd(split_1, lift[0][b], dist(t, split_1, split_1), 1); + } else if (split_1 == anc && split_2 == anc) { + // lca(a, t) and lca(b, t) are both lca(a, b) + upd(a, anc, dist(a, t, anc), -1); + upd(b, lift[0][anc], dist(b, t, anc), -1); + } else { + // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) + if (depth[split_1] < depth[split_2]) { + swap(split_1, split_2), swap(a, b); + } + upd(a, split_1, dist(a, t, split_1), -1); + upd(split_1, anc, dist(split_1, t, split_1), 1); + upd(b, lift[0][anc], dist(b, t, anc), -1); + } + } + }; + for (int i = 0; i < s; i++) { + int a, b, t; + cin >> a >> b >> t; + qry(a, b, t); + } + + // calculate the final result using the difference arrays + function dfs = [&](int u, int p) { + for (int v : adj[u]) { + if (v == p) { continue; } + dfs(v, u); + diff[u][0] += diff[v][0] + diff[v][1]; + diff[u][1] += diff[v][1]; + } + }; + dfs(1, 0); + for (int i = 1; i <= n; i++) { cout << diff[i][0] << " \n"[i == n]; } } ``` From 518b7c35059b85b74c7c1d438052ff898ab34c64 Mon Sep 17 00:00:00 2001 From: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com> Date: Fri, 3 May 2024 20:26:20 -0700 Subject: [PATCH 05/23] Update bts-hotcold.mdx --- solutions/platinum/bts-hotcold.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 74a766e960..9f4ca4e042 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -34,17 +34,18 @@ In this case, split the path updates into 3 segments. Let the lower of the two L be $l$, and the node which is an ancestor of $l$ be $a$. Then, just update the path from $a$ to $l$, $l$ to $p$, and the path from $b$ to $p$. -**Handling Path Updates** +### Handling Path Updates Note that based on how we did our casework on the paths, that all path updates are between a given node and its ancestor. Also, note that the path updates are just adding distances which either increase or decrease by a fixed amount every time. So, we can sort of do a difference array on a tree, where we DFS down the tree and apply the differences while walking back up the tree. +## Implementation + **Time Complexity:** $\mathcal{O}(N\log{N})$ - ```cpp @@ -181,5 +182,4 @@ int main() { ``` - From 58ae5f1413bd1745ae31bf6713df634409f4f077 Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Fri, 3 May 2024 21:22:20 -0700 Subject: [PATCH 06/23] put everything into a class --- solutions/platinum/bts-hotcold.mdx | 261 +++++++++++++++-------------- 1 file changed, 135 insertions(+), 126 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 9f4ca4e042..1392765ebd 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -14,7 +14,7 @@ is necessary. To do that, we use some casework. **Case 1: $p$ is equal to $t$, or $t$ is not inside $p$'s subtree.** -In this case, we just update the path from $a$ to $p$ and +In this case, we update the path from $a$ to $p$ and $b$ to $p$. Note that if $p$ is equal to either $a$ or $b$, then this case must be handled slightly differently. @@ -26,7 +26,8 @@ $a$ to $l$ and the path from $l$ to $b$. **Case 3: The LCA of $t$ with $a$ and $b$ is the same as $p$.** -Handle this case similarly to case 1. +Like how we handled case 1, for this case we update the path from $a$ to +$p$ and the path from $b$ to $p$. **Case 4: Either the LCA of $t$ and $a$ or the LCA of $t$ and $b$ lie on the path from $a$ to $b$.** @@ -53,131 +54,139 @@ down the tree and apply the differences while walking back up the tree. using namespace std; using ll = long long; +class Tree { + private: + const int n, LOG; + vector> adj, lift; + vector depth, tin, tout; + vector> diff; + int timer = 0; + public: + Tree(int _n) : n(_n), LOG((int) log2(n) + 1), adj(n), + lift(LOG, vector(n)), depth(n), + tin(n), tout(n), diff(n) { + tin[0] = -1; + tout[0] = n + 1; + // ^ ensures that LCA works + } + void addEdge(int u, int v) { + adj[u].push_back(v); + adj[v].push_back(u); + } + void build(int u, int p) { + tin[u] = timer++; + lift[0][u] = p; + depth[u] = depth[p] + 1; + for (int i = 1; i < LOG; i++) { + lift[i][u] = lift[i - 1][lift[i - 1][u]]; + } + for (int v : adj[u]) { + if (v != p) { build(v, u); } + } + tout[u] = timer - 1; + } + bool isAncestor(int u, int v) { + return tin[u] <= tin[v] && tout[v] <= tout[u]; + } + int lca(int u, int v) { + if (isAncestor(u, v)) { return u; } + if (isAncestor(v, u)) { return v; } + for (int i = LOG - 1; i >= 0; i--) { + if (!isAncestor(lift[i][u], v)) { + u = lift[i][u]; + } + } + return lift[0][u]; + } + int dist(int u, int v, int anc = -1) { + if (anc == -1) { anc = lca(u, v); } + return depth[u] + depth[v] - 2 * depth[anc]; + } + void update(int u, int v, int initial, int change) { + diff[u][0] += initial; + diff[u][1] += change; + if (change > 0) { + diff[v][0] -= initial + (depth[u] - depth[v]); + } else { + diff[v][0] -= initial - (depth[u] - depth[v]); + } + diff[v][1] -= change; + } + void query(int a, int b, int t) { + int anc = lca(a, b); + if (anc == t || !isAncestor(anc, t)) { + // t is either the LCA, or not inside the subtree of the LCA + if (anc == a || anc == b) { + if (anc == a) { swap(a, b); } + update(a, lift[0][b], dist(a, t), -1); + } else { + update(a, anc, dist(a, t), -1); + update(b, lift[0][anc], dist(b, t), -1); + } + } else { + // split_1 and split_2 are candidates for the locations where + // the path updates go from increasing to decreasing (or vice versa) + int split_1 = lca(a, t); + int split_2 = lca(b, t); + if (anc == a || anc == b) { + // path from a to be is just a walk upward + if (anc == a) { + swap(a, b); + swap(split_1, split_2); + } + update(a, split_1, dist(a, t, split_1), -1); + update(split_1, lift[0][b], dist(t, split_1, split_1), 1); + } else if (split_1 == anc && split_2 == anc) { + // lca(a, t) and lca(b, t) are both lca(a, b) + update(a, anc, dist(a, t, anc), -1); + update(b, lift[0][anc], dist(b, t, anc), -1); + } else { + // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) + if (depth[split_1] < depth[split_2]) { + swap(split_1, split_2), swap(a, b); + } + update(a, split_1, dist(a, t, split_1), -1); + update(split_1, anc, dist(split_1, t, split_1), 1); + update(b, lift[0][anc], dist(b, t, anc), -1); + } + } + } + void dfs(int u, int p) { + for (int v : adj[u]) { + if (v == p) { continue; } + dfs(v, u); + diff[u][0] += diff[v][0] + diff[v][1]; + diff[u][1] += diff[v][1]; + } + } + vector result() { + dfs(1, 0); + vector res(n + 1); + for (int i = 1; i <= n; i++) { + res[i] = diff[i][0]; + } + return res; + } +}; int main() { - int n, s; - cin >> n >> s; - // 1 indexing makes updates easier - vector> adj(n + 1); - for (int i = 1; i < n; i++) { - int u, v; - cin >> u >> v; - adj[u].push_back(v); - adj[v].push_back(u); - } - - // perform Euler tour and binary lifts - const int LOG = 1 + (int)log2(n); - vector> lift(LOG, vector(n + 1)); - vector tin(n + 1); - vector tout(n + 1); - vector depth(n + 1); - int timer = 0; - function tour = [&](int u, int p) { - tin[u] = timer++; - lift[0][u] = p; - depth[u] = depth[p] + 1; - for (int i = 1; i < LOG; i++) { - lift[i][u] = lift[i - 1][lift[i - 1][u]]; - } - for (int v : adj[u]) { - if (v != p) { tour(v, u); } - } - tout[u] = timer - 1; - }; - tour(1, 0); - tin[0] = -1, tout[0] = n + 1; // so LCA works - - // just some helper methods to simplify the code - auto isAncestor = [&](int u, int v) -> bool { - return tin[u] <= tin[v] && tout[v] <= tout[u]; - }; - auto lca = [&](int u, int v) -> int { - if (isAncestor(u, v)) { return u; } - if (isAncestor(v, u)) { return v; } - for (int i = LOG - 1; i >= 0; i--) { - if (!isAncestor(lift[i][u], v)) { u = lift[i][u]; } - } - return lift[0][u]; - }; - auto dist = [&](int a, int b, int anc = -1) -> int { - if (anc == -1) { anc = lca(a, b); } - return depth[a] + depth[b] - 2 * depth[anc]; - }; - - // calculating difference array updates - vector> diff(n + 1, {0, 0}); - auto upd = [&](int u, int v, int initial, int change) -> void { - /* - * Precondition: v is an ancestor of u, or equal to u. - * - * Updates path from u to v, excluding v. When walking up from - * u to v, the added initial value is updated with the "change". - */ - diff[u][0] += initial; - diff[u][1] += change; - if (change > 0) { - diff[v][0] -= initial + (depth[u] - depth[v]); - } else { - diff[v][0] -= initial - (depth[u] - depth[v]); - } - diff[v][1] -= change; - }; - auto qry = [&](int a, int b, int t) -> void { - int anc = lca(a, b); - if (anc == t || !isAncestor(anc, t)) { - // t is either the LCA, or not inside the subtree of the LCA - if (anc == a || anc == b) { - if (anc == a) { swap(a, b); } - upd(a, lift[0][b], dist(a, t), -1); - } else { - upd(a, anc, dist(a, t), -1); - upd(b, lift[0][anc], dist(b, t), -1); - } - } else { - // split_1 and split_2 are candidates for the locations where - // the path updates go from increasing to decreasing (or vice versa) - int split_1 = lca(a, t); - int split_2 = lca(b, t); - if (anc == a || anc == b) { - // path from a to be is just a walk upward - if (anc == a) { - swap(a, b); - swap(split_1, split_2); - } - upd(a, split_1, dist(a, t, split_1), -1); - upd(split_1, lift[0][b], dist(t, split_1, split_1), 1); - } else if (split_1 == anc && split_2 == anc) { - // lca(a, t) and lca(b, t) are both lca(a, b) - upd(a, anc, dist(a, t, anc), -1); - upd(b, lift[0][anc], dist(b, t, anc), -1); - } else { - // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) - if (depth[split_1] < depth[split_2]) { - swap(split_1, split_2), swap(a, b); - } - upd(a, split_1, dist(a, t, split_1), -1); - upd(split_1, anc, dist(split_1, t, split_1), 1); - upd(b, lift[0][anc], dist(b, t, anc), -1); - } - } - }; - for (int i = 0; i < s; i++) { - int a, b, t; - cin >> a >> b >> t; - qry(a, b, t); - } - - // calculate the final result using the difference arrays - function dfs = [&](int u, int p) { - for (int v : adj[u]) { - if (v == p) { continue; } - dfs(v, u); - diff[u][0] += diff[v][0] + diff[v][1]; - diff[u][1] += diff[v][1]; - } - }; - dfs(1, 0); - for (int i = 1; i <= n; i++) { cout << diff[i][0] << " \n"[i == n]; } + int n, s; + cin >> n >> s; + Tree tree(n + 1); + for (int i = 1; i < n; i++) { + int u, v; + cin >> u >> v; + tree.addEdge(u, v); + } + tree.build(1, 0); + for (int i = 0; i < s; i++) { + int a, b, t; + cin >> a >> b >> t; + tree.query(a, b, t); + } + vector res = tree.result(); + for (int i = 1; i <= n; i++) { + cout << res[i] << " \n"[i == n]; + } } ``` From fd3a169c7619ca3a33e6f57948aff9818c5864e6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 04:23:29 +0000 Subject: [PATCH 07/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/bts-hotcold.mdx | 255 ++++++++++++++--------------- 1 file changed, 125 insertions(+), 130 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 1392765ebd..ac2394779d 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -55,138 +55,133 @@ using namespace std; using ll = long long; class Tree { - private: - const int n, LOG; - vector> adj, lift; - vector depth, tin, tout; - vector> diff; - int timer = 0; - public: - Tree(int _n) : n(_n), LOG((int) log2(n) + 1), adj(n), - lift(LOG, vector(n)), depth(n), - tin(n), tout(n), diff(n) { - tin[0] = -1; - tout[0] = n + 1; - // ^ ensures that LCA works - } - void addEdge(int u, int v) { - adj[u].push_back(v); - adj[v].push_back(u); - } - void build(int u, int p) { - tin[u] = timer++; - lift[0][u] = p; - depth[u] = depth[p] + 1; - for (int i = 1; i < LOG; i++) { - lift[i][u] = lift[i - 1][lift[i - 1][u]]; - } - for (int v : adj[u]) { - if (v != p) { build(v, u); } - } - tout[u] = timer - 1; - } - bool isAncestor(int u, int v) { - return tin[u] <= tin[v] && tout[v] <= tout[u]; - } - int lca(int u, int v) { - if (isAncestor(u, v)) { return u; } - if (isAncestor(v, u)) { return v; } - for (int i = LOG - 1; i >= 0; i--) { - if (!isAncestor(lift[i][u], v)) { - u = lift[i][u]; - } - } - return lift[0][u]; - } - int dist(int u, int v, int anc = -1) { - if (anc == -1) { anc = lca(u, v); } - return depth[u] + depth[v] - 2 * depth[anc]; - } - void update(int u, int v, int initial, int change) { - diff[u][0] += initial; - diff[u][1] += change; - if (change > 0) { - diff[v][0] -= initial + (depth[u] - depth[v]); - } else { - diff[v][0] -= initial - (depth[u] - depth[v]); - } - diff[v][1] -= change; - } - void query(int a, int b, int t) { - int anc = lca(a, b); - if (anc == t || !isAncestor(anc, t)) { - // t is either the LCA, or not inside the subtree of the LCA - if (anc == a || anc == b) { - if (anc == a) { swap(a, b); } - update(a, lift[0][b], dist(a, t), -1); - } else { - update(a, anc, dist(a, t), -1); - update(b, lift[0][anc], dist(b, t), -1); - } - } else { - // split_1 and split_2 are candidates for the locations where - // the path updates go from increasing to decreasing (or vice versa) - int split_1 = lca(a, t); - int split_2 = lca(b, t); - if (anc == a || anc == b) { - // path from a to be is just a walk upward - if (anc == a) { - swap(a, b); - swap(split_1, split_2); - } - update(a, split_1, dist(a, t, split_1), -1); - update(split_1, lift[0][b], dist(t, split_1, split_1), 1); - } else if (split_1 == anc && split_2 == anc) { - // lca(a, t) and lca(b, t) are both lca(a, b) - update(a, anc, dist(a, t, anc), -1); - update(b, lift[0][anc], dist(b, t, anc), -1); - } else { - // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) - if (depth[split_1] < depth[split_2]) { - swap(split_1, split_2), swap(a, b); - } - update(a, split_1, dist(a, t, split_1), -1); - update(split_1, anc, dist(split_1, t, split_1), 1); - update(b, lift[0][anc], dist(b, t, anc), -1); - } - } - } - void dfs(int u, int p) { - for (int v : adj[u]) { - if (v == p) { continue; } - dfs(v, u); - diff[u][0] += diff[v][0] + diff[v][1]; - diff[u][1] += diff[v][1]; - } - } - vector result() { - dfs(1, 0); - vector res(n + 1); - for (int i = 1; i <= n; i++) { - res[i] = diff[i][0]; - } - return res; - } + private: + const int n, LOG; + vector> adj, lift; + vector depth, tin, tout; + vector> diff; + int timer = 0; + + public: + Tree(int _n) + : n(_n), LOG((int)log2(n) + 1), adj(n), lift(LOG, vector(n)), + depth(n), tin(n), tout(n), diff(n) { + tin[0] = -1; + tout[0] = n + 1; + // ^ ensures that LCA works + } + void addEdge(int u, int v) { + adj[u].push_back(v); + adj[v].push_back(u); + } + void build(int u, int p) { + tin[u] = timer++; + lift[0][u] = p; + depth[u] = depth[p] + 1; + for (int i = 1; i < LOG; i++) { + lift[i][u] = lift[i - 1][lift[i - 1][u]]; + } + for (int v : adj[u]) { + if (v != p) { build(v, u); } + } + tout[u] = timer - 1; + } + bool isAncestor(int u, int v) { + return tin[u] <= tin[v] && tout[v] <= tout[u]; + } + int lca(int u, int v) { + if (isAncestor(u, v)) { return u; } + if (isAncestor(v, u)) { return v; } + for (int i = LOG - 1; i >= 0; i--) { + if (!isAncestor(lift[i][u], v)) { u = lift[i][u]; } + } + return lift[0][u]; + } + int dist(int u, int v, int anc = -1) { + if (anc == -1) { anc = lca(u, v); } + return depth[u] + depth[v] - 2 * depth[anc]; + } + void update(int u, int v, int initial, int change) { + diff[u][0] += initial; + diff[u][1] += change; + if (change > 0) { + diff[v][0] -= initial + (depth[u] - depth[v]); + } else { + diff[v][0] -= initial - (depth[u] - depth[v]); + } + diff[v][1] -= change; + } + void query(int a, int b, int t) { + int anc = lca(a, b); + if (anc == t || !isAncestor(anc, t)) { + // t is either the LCA, or not inside the subtree of the LCA + if (anc == a || anc == b) { + if (anc == a) { swap(a, b); } + update(a, lift[0][b], dist(a, t), -1); + } else { + update(a, anc, dist(a, t), -1); + update(b, lift[0][anc], dist(b, t), -1); + } + } else { + // split_1 and split_2 are candidates for the locations where + // the path updates go from increasing to decreasing (or vice versa) + int split_1 = lca(a, t); + int split_2 = lca(b, t); + if (anc == a || anc == b) { + // path from a to be is just a walk upward + if (anc == a) { + swap(a, b); + swap(split_1, split_2); + } + update(a, split_1, dist(a, t, split_1), -1); + update(split_1, lift[0][b], dist(t, split_1, split_1), 1); + } else if (split_1 == anc && split_2 == anc) { + // lca(a, t) and lca(b, t) are both lca(a, b) + update(a, anc, dist(a, t, anc), -1); + update(b, lift[0][anc], dist(b, t, anc), -1); + } else { + // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) + if (depth[split_1] < depth[split_2]) { + swap(split_1, split_2), swap(a, b); + } + update(a, split_1, dist(a, t, split_1), -1); + update(split_1, anc, dist(split_1, t, split_1), 1); + update(b, lift[0][anc], dist(b, t, anc), -1); + } + } + } + void dfs(int u, int p) { + for (int v : adj[u]) { + if (v == p) { continue; } + dfs(v, u); + diff[u][0] += diff[v][0] + diff[v][1]; + diff[u][1] += diff[v][1]; + } + } + vector result() { + dfs(1, 0); + vector res(n + 1); + for (int i = 1; i <= n; i++) { res[i] = diff[i][0]; } + return res; + } }; int main() { - int n, s; - cin >> n >> s; - Tree tree(n + 1); - for (int i = 1; i < n; i++) { - int u, v; - cin >> u >> v; - tree.addEdge(u, v); - } - tree.build(1, 0); - for (int i = 0; i < s; i++) { - int a, b, t; - cin >> a >> b >> t; - tree.query(a, b, t); - } - vector res = tree.result(); - for (int i = 1; i <= n; i++) { - cout << res[i] << " \n"[i == n]; - } + int n, s; + cin >> n >> s; + Tree tree(n + 1); + for (int i = 1; i < n; i++) { + int u, v; + cin >> u >> v; + tree.addEdge(u, v); + } + tree.build(1, 0); + for (int i = 0; i < s; i++) { + int a, b, t; + cin >> a >> b >> t; + tree.query(a, b, t); + } + vector res = tree.result(); + for (int i = 1; i <= n; i++) { cout << res[i] << " \n"[i == n]; } } ``` From fbc2ae3929090218edabb92802c6891d014186c7 Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Sat, 4 May 2024 10:06:42 -0700 Subject: [PATCH 08/23] changed some stuff --- solutions/platinum/bts-hotcold.mdx | 54 ++++++++++++++++++------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 1392765ebd..cff7f6f833 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -56,28 +56,30 @@ using ll = long long; class Tree { private: - const int n, LOG; - vector> adj, lift; - vector depth, tin, tout; + const int n, log2dist; + vector> &adj; + vector> lift; vector> diff; + vector depth; + vector tin; + vector tout; int timer = 0; public: - Tree(int _n) : n(_n), LOG((int) log2(n) + 1), adj(n), - lift(LOG, vector(n)), depth(n), - tin(n), tout(n), diff(n) { + Tree(int n, vector> &adj) + : n(n), log2dist((int) log2(n) + 1), adj(adj), + lift(log2dist, vector(n)), depth(n), + tin(n), tout(n), diff(n) { tin[0] = -1; tout[0] = n + 1; // ^ ensures that LCA works + build(1, 0); } - void addEdge(int u, int v) { - adj[u].push_back(v); - adj[v].push_back(u); - } + void build(int u, int p) { tin[u] = timer++; lift[0][u] = p; depth[u] = depth[p] + 1; - for (int i = 1; i < LOG; i++) { + for (int i = 1; i < log2dist; i++) { lift[i][u] = lift[i - 1][lift[i - 1][u]]; } for (int v : adj[u]) { @@ -85,23 +87,27 @@ class Tree { } tout[u] = timer - 1; } - bool isAncestor(int u, int v) { + + bool is_ancestor(int u, int v) { return tin[u] <= tin[v] && tout[v] <= tout[u]; } + int lca(int u, int v) { - if (isAncestor(u, v)) { return u; } - if (isAncestor(v, u)) { return v; } - for (int i = LOG - 1; i >= 0; i--) { - if (!isAncestor(lift[i][u], v)) { + if (is_ancestor(u, v)) { return u; } + if (is_ancestor(v, u)) { return v; } + for (int i = log2dist - 1; i >= 0; i--) { + if (!is_ancestor(lift[i][u], v)) { u = lift[i][u]; } } return lift[0][u]; } + int dist(int u, int v, int anc = -1) { if (anc == -1) { anc = lca(u, v); } return depth[u] + depth[v] - 2 * depth[anc]; } + void update(int u, int v, int initial, int change) { diff[u][0] += initial; diff[u][1] += change; @@ -112,9 +118,10 @@ class Tree { } diff[v][1] -= change; } + void query(int a, int b, int t) { int anc = lca(a, b); - if (anc == t || !isAncestor(anc, t)) { + if (anc == t || !is_ancestor(anc, t)) { // t is either the LCA, or not inside the subtree of the LCA if (anc == a || anc == b) { if (anc == a) { swap(a, b); } @@ -151,6 +158,7 @@ class Tree { } } } + void dfs(int u, int p) { for (int v : adj[u]) { if (v == p) { continue; } @@ -159,7 +167,8 @@ class Tree { diff[u][1] += diff[v][1]; } } - vector result() { + + vector calculate_result() { dfs(1, 0); vector res(n + 1); for (int i = 1; i <= n; i++) { @@ -171,19 +180,20 @@ class Tree { int main() { int n, s; cin >> n >> s; - Tree tree(n + 1); + vector> adj(n + 1); for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; - tree.addEdge(u, v); + adj[u].push_back(v); + adj[v].push_back(u); } - tree.build(1, 0); + Tree tree(n + 1, adj); for (int i = 0; i < s; i++) { int a, b, t; cin >> a >> b >> t; tree.query(a, b, t); } - vector res = tree.result(); + vector res = tree.calculate_result(); for (int i = 1; i <= n; i++) { cout << res[i] << " \n"[i == n]; } From 073620d0dde64eec00184d55c73a80136d217c91 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 17:10:53 +0000 Subject: [PATCH 09/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/bts-hotcold.mdx | 274 ++++++++++++++--------------- 1 file changed, 134 insertions(+), 140 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index cff7f6f833..5da69f1ac9 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -55,148 +55,142 @@ using namespace std; using ll = long long; class Tree { - private: - const int n, log2dist; - vector> &adj; - vector> lift; - vector> diff; - vector depth; - vector tin; - vector tout; - int timer = 0; - public: - Tree(int n, vector> &adj) - : n(n), log2dist((int) log2(n) + 1), adj(adj), - lift(log2dist, vector(n)), depth(n), - tin(n), tout(n), diff(n) { - tin[0] = -1; - tout[0] = n + 1; - // ^ ensures that LCA works - build(1, 0); - } - - void build(int u, int p) { - tin[u] = timer++; - lift[0][u] = p; - depth[u] = depth[p] + 1; - for (int i = 1; i < log2dist; i++) { - lift[i][u] = lift[i - 1][lift[i - 1][u]]; - } - for (int v : adj[u]) { - if (v != p) { build(v, u); } - } - tout[u] = timer - 1; - } - - bool is_ancestor(int u, int v) { - return tin[u] <= tin[v] && tout[v] <= tout[u]; - } - - int lca(int u, int v) { - if (is_ancestor(u, v)) { return u; } - if (is_ancestor(v, u)) { return v; } - for (int i = log2dist - 1; i >= 0; i--) { - if (!is_ancestor(lift[i][u], v)) { - u = lift[i][u]; - } - } - return lift[0][u]; - } - - int dist(int u, int v, int anc = -1) { - if (anc == -1) { anc = lca(u, v); } - return depth[u] + depth[v] - 2 * depth[anc]; - } - - void update(int u, int v, int initial, int change) { - diff[u][0] += initial; - diff[u][1] += change; - if (change > 0) { - diff[v][0] -= initial + (depth[u] - depth[v]); - } else { - diff[v][0] -= initial - (depth[u] - depth[v]); - } - diff[v][1] -= change; - } - - void query(int a, int b, int t) { - int anc = lca(a, b); - if (anc == t || !is_ancestor(anc, t)) { - // t is either the LCA, or not inside the subtree of the LCA - if (anc == a || anc == b) { - if (anc == a) { swap(a, b); } - update(a, lift[0][b], dist(a, t), -1); - } else { - update(a, anc, dist(a, t), -1); - update(b, lift[0][anc], dist(b, t), -1); - } - } else { - // split_1 and split_2 are candidates for the locations where - // the path updates go from increasing to decreasing (or vice versa) - int split_1 = lca(a, t); - int split_2 = lca(b, t); - if (anc == a || anc == b) { - // path from a to be is just a walk upward - if (anc == a) { - swap(a, b); - swap(split_1, split_2); - } - update(a, split_1, dist(a, t, split_1), -1); - update(split_1, lift[0][b], dist(t, split_1, split_1), 1); - } else if (split_1 == anc && split_2 == anc) { - // lca(a, t) and lca(b, t) are both lca(a, b) - update(a, anc, dist(a, t, anc), -1); - update(b, lift[0][anc], dist(b, t, anc), -1); - } else { - // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) - if (depth[split_1] < depth[split_2]) { - swap(split_1, split_2), swap(a, b); - } - update(a, split_1, dist(a, t, split_1), -1); - update(split_1, anc, dist(split_1, t, split_1), 1); - update(b, lift[0][anc], dist(b, t, anc), -1); - } - } - } - - void dfs(int u, int p) { - for (int v : adj[u]) { - if (v == p) { continue; } - dfs(v, u); - diff[u][0] += diff[v][0] + diff[v][1]; - diff[u][1] += diff[v][1]; - } - } - - vector calculate_result() { - dfs(1, 0); - vector res(n + 1); - for (int i = 1; i <= n; i++) { - res[i] = diff[i][0]; - } - return res; - } + private: + const int n, log2dist; + vector> &adj; + vector> lift; + vector> diff; + vector depth; + vector tin; + vector tout; + int timer = 0; + + public: + Tree(int n, vector> &adj) + : n(n), log2dist((int)log2(n) + 1), adj(adj), + lift(log2dist, vector(n)), depth(n), tin(n), tout(n), diff(n) { + tin[0] = -1; + tout[0] = n + 1; + // ^ ensures that LCA works + build(1, 0); + } + + void build(int u, int p) { + tin[u] = timer++; + lift[0][u] = p; + depth[u] = depth[p] + 1; + for (int i = 1; i < log2dist; i++) { + lift[i][u] = lift[i - 1][lift[i - 1][u]]; + } + for (int v : adj[u]) { + if (v != p) { build(v, u); } + } + tout[u] = timer - 1; + } + + bool is_ancestor(int u, int v) { + return tin[u] <= tin[v] && tout[v] <= tout[u]; + } + + int lca(int u, int v) { + if (is_ancestor(u, v)) { return u; } + if (is_ancestor(v, u)) { return v; } + for (int i = log2dist - 1; i >= 0; i--) { + if (!is_ancestor(lift[i][u], v)) { u = lift[i][u]; } + } + return lift[0][u]; + } + + int dist(int u, int v, int anc = -1) { + if (anc == -1) { anc = lca(u, v); } + return depth[u] + depth[v] - 2 * depth[anc]; + } + + void update(int u, int v, int initial, int change) { + diff[u][0] += initial; + diff[u][1] += change; + if (change > 0) { + diff[v][0] -= initial + (depth[u] - depth[v]); + } else { + diff[v][0] -= initial - (depth[u] - depth[v]); + } + diff[v][1] -= change; + } + + void query(int a, int b, int t) { + int anc = lca(a, b); + if (anc == t || !is_ancestor(anc, t)) { + // t is either the LCA, or not inside the subtree of the LCA + if (anc == a || anc == b) { + if (anc == a) { swap(a, b); } + update(a, lift[0][b], dist(a, t), -1); + } else { + update(a, anc, dist(a, t), -1); + update(b, lift[0][anc], dist(b, t), -1); + } + } else { + // split_1 and split_2 are candidates for the locations where + // the path updates go from increasing to decreasing (or vice versa) + int split_1 = lca(a, t); + int split_2 = lca(b, t); + if (anc == a || anc == b) { + // path from a to be is just a walk upward + if (anc == a) { + swap(a, b); + swap(split_1, split_2); + } + update(a, split_1, dist(a, t, split_1), -1); + update(split_1, lift[0][b], dist(t, split_1, split_1), 1); + } else if (split_1 == anc && split_2 == anc) { + // lca(a, t) and lca(b, t) are both lca(a, b) + update(a, anc, dist(a, t, anc), -1); + update(b, lift[0][anc], dist(b, t, anc), -1); + } else { + // lca(a, b) != lca(a, t), or lca(a, b) != lca(b, t) + if (depth[split_1] < depth[split_2]) { + swap(split_1, split_2), swap(a, b); + } + update(a, split_1, dist(a, t, split_1), -1); + update(split_1, anc, dist(split_1, t, split_1), 1); + update(b, lift[0][anc], dist(b, t, anc), -1); + } + } + } + + void dfs(int u, int p) { + for (int v : adj[u]) { + if (v == p) { continue; } + dfs(v, u); + diff[u][0] += diff[v][0] + diff[v][1]; + diff[u][1] += diff[v][1]; + } + } + + vector calculate_result() { + dfs(1, 0); + vector res(n + 1); + for (int i = 1; i <= n; i++) { res[i] = diff[i][0]; } + return res; + } }; int main() { - int n, s; - cin >> n >> s; - vector> adj(n + 1); - for (int i = 1; i < n; i++) { - int u, v; - cin >> u >> v; - adj[u].push_back(v); - adj[v].push_back(u); - } - Tree tree(n + 1, adj); - for (int i = 0; i < s; i++) { - int a, b, t; - cin >> a >> b >> t; - tree.query(a, b, t); - } - vector res = tree.calculate_result(); - for (int i = 1; i <= n; i++) { - cout << res[i] << " \n"[i == n]; - } + int n, s; + cin >> n >> s; + vector> adj(n + 1); + for (int i = 1; i < n; i++) { + int u, v; + cin >> u >> v; + adj[u].push_back(v); + adj[v].push_back(u); + } + Tree tree(n + 1, adj); + for (int i = 0; i < s; i++) { + int a, b, t; + cin >> a >> b >> t; + tree.query(a, b, t); + } + vector res = tree.calculate_result(); + for (int i = 1; i <= n; i++) { cout << res[i] << " \n"[i == n]; } } ``` From 9400b9be5a9a4fca2354cfff1c0a39a0bac17e0e Mon Sep 17 00:00:00 2001 From: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com> Date: Sat, 4 May 2024 12:34:35 -0700 Subject: [PATCH 10/23] Update bts-hotcold.mdx --- solutions/platinum/bts-hotcold.mdx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 5da69f1ac9..c2f63b9b7e 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -56,7 +56,8 @@ using ll = long long; class Tree { private: - const int n, log2dist; + const int n; + const int log2dist; vector> &adj; vector> lift; vector> diff; @@ -173,22 +174,26 @@ class Tree { return res; } }; + int main() { - int n, s; + int n; + int s; cin >> n >> s; vector> adj(n + 1); - for (int i = 1; i < n; i++) { + for (int i = 0; i < n - 1; i++) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); } + Tree tree(n + 1, adj); for (int i = 0; i < s; i++) { int a, b, t; cin >> a >> b >> t; tree.query(a, b, t); } + vector res = tree.calculate_result(); for (int i = 1; i <= n; i++) { cout << res[i] << " \n"[i == n]; } } From 281997c481e3e66ca65fb00948a37ab9778420da Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Sat, 4 May 2024 13:47:37 -0700 Subject: [PATCH 11/23] Update solutions/platinum/bts-hotcold.mdx Co-authored-by: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com> --- solutions/platinum/bts-hotcold.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index c2f63b9b7e..470f0f7bb4 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -71,8 +71,7 @@ class Tree { : n(n), log2dist((int)log2(n) + 1), adj(adj), lift(log2dist, vector(n)), depth(n), tin(n), tout(n), diff(n) { tin[0] = -1; - tout[0] = n + 1; - // ^ ensures that LCA works + tout[0] = n + 1; // ensures that LCA works build(1, 0); } From 8fb12fc1d6e481bba932435004d1de903414f0f0 Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Sat, 4 May 2024 13:48:50 -0700 Subject: [PATCH 12/23] Update solutions/platinum/bts-hotcold.mdx Co-authored-by: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com> --- solutions/platinum/bts-hotcold.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 470f0f7bb4..2fcecf51f4 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -59,7 +59,7 @@ class Tree { const int n; const int log2dist; vector> &adj; - vector> lift; + vector> lift; // for binary lifting vector> diff; vector depth; vector tin; From ee21978388fab8754c154f88e447e0e4f8612ec4 Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Sat, 4 May 2024 13:53:50 -0700 Subject: [PATCH 13/23] made constructor a bit more detailed n stuff --- solutions/platinum/bts-hotcold.mdx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 2fcecf51f4..df3213bfc3 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -57,11 +57,13 @@ using ll = long long; class Tree { private: const int n; - const int log2dist; - vector> &adj; + const int log2dist; // max height of the tree + vector> &adj; // reference to adjacency list vector> lift; // for binary lifting - vector> diff; - vector depth; + vector> diff; // difference array updates + vector depth; // depth of each node + + // these are for Euler tour vector tin; vector tout; int timer = 0; From cce5999985750992e23ce16859a6e46cb0578c7b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 20:55:03 +0000 Subject: [PATCH 14/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/bts-hotcold.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index df3213bfc3..8d5c734306 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -57,12 +57,12 @@ using ll = long long; class Tree { private: const int n; - const int log2dist; // max height of the tree - vector> &adj; // reference to adjacency list - vector> lift; // for binary lifting - vector> diff; // difference array updates - vector depth; // depth of each node - + const int log2dist; // max height of the tree + vector> &adj; // reference to adjacency list + vector> lift; // for binary lifting + vector> diff; // difference array updates + vector depth; // depth of each node + // these are for Euler tour vector tin; vector tout; From 73e0ec02fc0f90b178bee2fd6f75761a25c5814b Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Sat, 4 May 2024 16:22:33 -0700 Subject: [PATCH 15/23] Update bts-hotcold.mdx --- solutions/platinum/bts-hotcold.mdx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 8d5c734306..674bd3022e 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -57,7 +57,7 @@ using ll = long long; class Tree { private: const int n; - const int log2dist; // max height of the tree + const int log2dist; // # of bits needed for binary lift vector> &adj; // reference to adjacency list vector> lift; // for binary lifting vector> diff; // difference array updates @@ -68,16 +68,7 @@ class Tree { vector tout; int timer = 0; - public: - Tree(int n, vector> &adj) - : n(n), log2dist((int)log2(n) + 1), adj(adj), - lift(log2dist, vector(n)), depth(n), tin(n), tout(n), diff(n) { - tin[0] = -1; - tout[0] = n + 1; // ensures that LCA works - build(1, 0); - } - - void build(int u, int p) { + void tour(int u, int p) { tin[u] = timer++; lift[0][u] = p; depth[u] = depth[p] + 1; @@ -85,11 +76,20 @@ class Tree { lift[i][u] = lift[i - 1][lift[i - 1][u]]; } for (int v : adj[u]) { - if (v != p) { build(v, u); } + if (v != p) { tour(v, u); } } tout[u] = timer - 1; } + public: + Tree(int n, vector> &adj) + : n(n), log2dist((int)log2(n) + 1), adj(adj), + lift(log2dist, vector(n)), diff(n), depth(n), tin(n), tout(n) { + tin[0] = -1; + tout[0] = n + 1; // ensures that LCA works + tour(1, 0); + } + bool is_ancestor(int u, int v) { return tin[u] <= tin[v] && tout[v] <= tout[u]; } From 26859fa8c6872dd35fd09fa0f5009cfeb2e1799f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 23:23:45 +0000 Subject: [PATCH 16/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/bts-hotcold.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 674bd3022e..654f995135 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -68,7 +68,7 @@ class Tree { vector tout; int timer = 0; - void tour(int u, int p) { + void tour(int u, int p) { tin[u] = timer++; lift[0][u] = p; depth[u] = depth[p] + 1; From c3224b559f5c3d8f162494cf6b7052ed7cd423a9 Mon Sep 17 00:00:00 2001 From: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com> Date: Sat, 4 May 2024 16:42:45 -0700 Subject: [PATCH 17/23] Update bts-hotcold.mdx --- solutions/platinum/bts-hotcold.mdx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 654f995135..595fff8919 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -81,6 +81,15 @@ class Tree { tout[u] = timer - 1; } + void dfs(int u, int p) { + for (int v : adj[u]) { + if (v == p) { continue; } + dfs(v, u); + diff[u][0] += diff[v][0] + diff[v][1]; + diff[u][1] += diff[v][1]; + } + } + public: Tree(int n, vector> &adj) : n(n), log2dist((int)log2(n) + 1), adj(adj), @@ -90,11 +99,11 @@ class Tree { tour(1, 0); } - bool is_ancestor(int u, int v) { + bool is_ancestor(int u, int v) const { return tin[u] <= tin[v] && tout[v] <= tout[u]; } - int lca(int u, int v) { + int lca(int u, int v) const { if (is_ancestor(u, v)) { return u; } if (is_ancestor(v, u)) { return v; } for (int i = log2dist - 1; i >= 0; i--) { @@ -103,7 +112,7 @@ class Tree { return lift[0][u]; } - int dist(int u, int v, int anc = -1) { + int dist(int u, int v, int anc = -1) const { if (anc == -1) { anc = lca(u, v); } return depth[u] + depth[v] - 2 * depth[anc]; } @@ -159,15 +168,6 @@ class Tree { } } - void dfs(int u, int p) { - for (int v : adj[u]) { - if (v == p) { continue; } - dfs(v, u); - diff[u][0] += diff[v][0] + diff[v][1]; - diff[u][1] += diff[v][1]; - } - } - vector calculate_result() { dfs(1, 0); vector res(n + 1); From c1a1f2f699e1a79b287bdd56765773677e5c2538 Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Sat, 4 May 2024 16:49:49 -0700 Subject: [PATCH 18/23] added lil comment --- solutions/platinum/bts-hotcold.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 674bd3022e..d27956bfab 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -103,6 +103,8 @@ class Tree { return lift[0][u]; } + // calculates distance from u to anc, and then to v + // if no ancestor is provided, anc = lca(u, v) int dist(int u, int v, int anc = -1) { if (anc == -1) { anc = lca(u, v); } return depth[u] + depth[v] - 2 * depth[anc]; From 4e9fbcbd7f309f96c6ceb1796200d6c4a745414c Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Sat, 4 May 2024 16:51:19 -0700 Subject: [PATCH 19/23] added comment --- solutions/platinum/bts-hotcold.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 674bd3022e..42f78baf4e 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -103,6 +103,8 @@ class Tree { return lift[0][u]; } + // calculates distance from u to anc, and then anc to v + // if anc is not provided, anc is set to lca(u, v) int dist(int u, int v, int anc = -1) { if (anc == -1) { anc = lca(u, v); } return depth[u] + depth[v] - 2 * depth[anc]; From 3aeacd5746416603a56a4351ce0286b9bfe5ab7d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 23:54:13 +0000 Subject: [PATCH 20/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- solutions/platinum/bts-hotcold.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 42f78baf4e..37c5232d7b 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -68,7 +68,7 @@ class Tree { vector tout; int timer = 0; - void tour(int u, int p) { + void tour(int u, int p) { tin[u] = timer++; lift[0][u] = p; depth[u] = depth[p] + 1; @@ -103,7 +103,7 @@ class Tree { return lift[0][u]; } - // calculates distance from u to anc, and then anc to v + // calculates distance from u to anc, and then anc to v // if anc is not provided, anc is set to lca(u, v) int dist(int u, int v, int anc = -1) { if (anc == -1) { anc = lca(u, v); } From 9277b339971451240885712f92d7d284bbf1400c Mon Sep 17 00:00:00 2001 From: TheGamingMousse <68484800+TheGamingMousse@users.noreply.github.com> Date: Sat, 4 May 2024 17:19:34 -0700 Subject: [PATCH 21/23] fixed my oopsie lol --- solutions/platinum/bts-hotcold.mdx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 42f78baf4e..37952395e9 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -68,7 +68,7 @@ class Tree { vector tout; int timer = 0; - void tour(int u, int p) { + void tour(int u, int p) { tin[u] = timer++; lift[0][u] = p; depth[u] = depth[p] + 1; @@ -81,6 +81,15 @@ class Tree { tout[u] = timer - 1; } + void dfs(int u, int p) { + for (int v : adj[u]) { + if (v == p) { continue; } + dfs(v, u); + diff[u][0] += diff[v][0] + diff[v][1]; + diff[u][1] += diff[v][1]; + } + } + public: Tree(int n, vector> &adj) : n(n), log2dist((int)log2(n) + 1), adj(adj), @@ -90,11 +99,11 @@ class Tree { tour(1, 0); } - bool is_ancestor(int u, int v) { + bool is_ancestor(int u, int v) const { return tin[u] <= tin[v] && tout[v] <= tout[u]; } - int lca(int u, int v) { + int lca(int u, int v) const { if (is_ancestor(u, v)) { return u; } if (is_ancestor(v, u)) { return v; } for (int i = log2dist - 1; i >= 0; i--) { @@ -105,7 +114,7 @@ class Tree { // calculates distance from u to anc, and then anc to v // if anc is not provided, anc is set to lca(u, v) - int dist(int u, int v, int anc = -1) { + int dist(int u, int v, int anc = -1) const { if (anc == -1) { anc = lca(u, v); } return depth[u] + depth[v] - 2 * depth[anc]; } @@ -161,15 +170,6 @@ class Tree { } } - void dfs(int u, int p) { - for (int v : adj[u]) { - if (v == p) { continue; } - dfs(v, u); - diff[u][0] += diff[v][0] + diff[v][1]; - diff[u][1] += diff[v][1]; - } - } - vector calculate_result() { dfs(1, 0); vector res(n + 1); From 0710999731749e7db39d41c9b90b804217764a7f Mon Sep 17 00:00:00 2001 From: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com> Date: Sat, 4 May 2024 17:33:14 -0700 Subject: [PATCH 22/23] Update bts-hotcold.mdx --- solutions/platinum/bts-hotcold.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index 434586a341..b161c66a14 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -112,8 +112,10 @@ class Tree { return lift[0][u]; } - // calculates distance from u to anc, and then anc to v - // if anc is not provided, anc is set to lca(u, v) + /** + * @return the distance from u to anc, and then anc to v + * if anc is not provided, it's set to lca(u, v) + */ int dist(int u, int v, int anc = -1) const { if (anc == -1) { anc = lca(u, v); } return depth[u] + depth[v] - 2 * depth[anc]; From deaad3a2d90a1e85561a75481331df6c11aca14e Mon Sep 17 00:00:00 2001 From: SansPapyrus683 <55369003+SansPapyrus683@users.noreply.github.com> Date: Sat, 4 May 2024 17:33:39 -0700 Subject: [PATCH 23/23] Update bts-hotcold.mdx --- solutions/platinum/bts-hotcold.mdx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/solutions/platinum/bts-hotcold.mdx b/solutions/platinum/bts-hotcold.mdx index b161c66a14..4b7bfbab0f 100644 --- a/solutions/platinum/bts-hotcold.mdx +++ b/solutions/platinum/bts-hotcold.mdx @@ -81,6 +81,17 @@ class Tree { tout[u] = timer - 1; } + void update(int u, int v, int initial, int change) { + diff[u][0] += initial; + diff[u][1] += change; + if (change > 0) { + diff[v][0] -= initial + (depth[u] - depth[v]); + } else { + diff[v][0] -= initial - (depth[u] - depth[v]); + } + diff[v][1] -= change; + } + void dfs(int u, int p) { for (int v : adj[u]) { if (v == p) { continue; } @@ -121,17 +132,6 @@ class Tree { return depth[u] + depth[v] - 2 * depth[anc]; } - void update(int u, int v, int initial, int change) { - diff[u][0] += initial; - diff[u][1] += change; - if (change > 0) { - diff[v][0] -= initial + (depth[u] - depth[v]); - } else { - diff[v][0] -= initial - (depth[u] - depth[v]); - } - diff[v][1] -= change; - } - void query(int a, int b, int t) { int anc = lca(a, b); if (anc == t || !is_ancestor(anc, t)) {