Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Editorial for problem Hot & Cold #4462

Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0160020
wrote the stuff
TheGamingMousse May 3, 2024
248753e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 3, 2024
872a572
did some style changes
TheGamingMousse May 4, 2024
954ebc8
Merge branch 'Editorial-for-BTS-17-Hot-&-Cold' of https://github.com/…
TheGamingMousse May 4, 2024
3a57e62
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 4, 2024
518b7c3
Update bts-hotcold.mdx
SansPapyrus683 May 4, 2024
58ae5f1
put everything into a class
TheGamingMousse May 4, 2024
fd3a169
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 4, 2024
fbc2ae3
changed some stuff
TheGamingMousse May 4, 2024
c634527
Merge branch 'master' into Editorial-for-BTS-17-Hot-&-Cold
TheGamingMousse May 4, 2024
b8d81cd
changed some stuff
TheGamingMousse May 4, 2024
48ac89a
Merge branch 'Editorial-for-BTS-17-Hot-&-Cold' of https://github.com/…
TheGamingMousse May 4, 2024
073620d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 4, 2024
9400b9b
Update bts-hotcold.mdx
SansPapyrus683 May 4, 2024
281997c
Update solutions/platinum/bts-hotcold.mdx
TheGamingMousse May 4, 2024
8fb12fc
Update solutions/platinum/bts-hotcold.mdx
TheGamingMousse May 4, 2024
ee21978
made constructor a bit more detailed n stuff
TheGamingMousse May 4, 2024
cce5999
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 4, 2024
73e0ec0
Update bts-hotcold.mdx
TheGamingMousse May 4, 2024
26859fa
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 4, 2024
c3224b5
Update bts-hotcold.mdx
SansPapyrus683 May 4, 2024
c1a1f2f
added lil comment
TheGamingMousse May 4, 2024
cda0afe
Merge branch 'Editorial-for-BTS-17-Hot-&-Cold' of https://github.com/…
TheGamingMousse May 4, 2024
4e9fbcb
added comment
TheGamingMousse May 4, 2024
3aeacd5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 4, 2024
9277b33
fixed my oopsie lol
TheGamingMousse May 5, 2024
cbd57e0
Merge branch 'Editorial-for-BTS-17-Hot-&-Cold' of https://github.com/…
TheGamingMousse May 5, 2024
0710999
Update bts-hotcold.mdx
SansPapyrus683 May 5, 2024
deaad3a
Update bts-hotcold.mdx
SansPapyrus683 May 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions content/5_Plat/Binary_Jump.problems.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,7 @@
"isStarred": false,
"tags": ["LCA"],
"solutionMetadata": {
"kind": "autogen-label-from-site",
"site": "DMOJ"
"kind": "internal"
}
},
{
Expand Down
189 changes: 189 additions & 0 deletions solutions/platinum/bts-hotcold.mdx
Original file line number Diff line number Diff line change
@@ -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 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$.**

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$.**

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.

## Implementation

**Time Complexity:** $\mathcal{O}(N\log{N})$

<LanguageSection>
<CPPSection>

```cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

class Tree {
private:
const int n, LOG;
vector<vector<int>> adj, lift;
vector<int> depth, tin, tout;
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
vector<array<ll, 2>> diff;
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
int timer = 0;

public:
Tree(int _n)
: n(_n), LOG((int)log2(n) + 1), adj(n), lift(LOG, vector<int>(n)),
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
depth(n), tin(n), tout(n), diff(n) {
tin[0] = -1;
tout[0] = n + 1;
// ^ ensures that LCA works
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void build(int u, int p) {
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
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) {
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
return tin[u] <= tin[v] && tout[v] <= tout[u];
}
int lca(int u, int v) {
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
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<ll> result() {
dfs(1, 0);
TheGamingMousse marked this conversation as resolved.
Show resolved Hide resolved
vector<ll> 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<ll> res = tree.result();
for (int i = 1; i <= n; i++) { cout << res[i] << " \n"[i == n]; }
}
```

</CPPSection>
</LanguageSection>
Loading