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

Create spoj-cot.mdx (solution for the problem I added about persisten… #5073

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fe06370
Create spoj-cot.mdx (solution for the problem I added about persisten…
1Nigar357 Jan 22, 2025
53a37c1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 22, 2025
9a8a775
Update spoj-cot.mdx
1Nigar357 Jan 22, 2025
997b2e1
Update spoj-cot.mdx
TheGamingMousse Jan 22, 2025
daddec5
Update Persistent.problems.json (change the kind in the solution meta…
1Nigar357 Jan 23, 2025
0ddcbd2
Update solutions/advanced/spoj-cot.mdx
1Nigar357 Jan 23, 2025
b872dea
Update spoj-cot.mdx
1Nigar357 Jan 23, 2025
1dc48cf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2025
0b80073
Update spoj-cot.mdx (Modified the explanation part and organized the …
1Nigar357 Jan 23, 2025
6ffd62c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2025
3a0ace6
Update spoj-cot.mdx
1Nigar357 Jan 23, 2025
0114d34
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2025
43ecc8c
Update spoj-cot.mdx
1Nigar357 Jan 23, 2025
9878da2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2025
74478a6
Update solutions/advanced/spoj-cot.mdx
1Nigar357 Jan 23, 2025
cb7950b
Update solutions/advanced/spoj-cot.mdx
1Nigar357 Jan 23, 2025
ea05116
Update spoj-cot.mdx
1Nigar357 Jan 23, 2025
8933218
Update spoj-cot.mdx
1Nigar357 Jan 24, 2025
5f43b1e
Update spoj-cot.mdx
1Nigar357 Jan 24, 2025
70012c1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 24, 2025
c205dd5
Create example1.png
1Nigar357 Jan 24, 2025
9032de1
Add files via upload
1Nigar357 Jan 24, 2025
46c28cc
Rename Screenshot 2025-01-24 at 1.39.50 AM.png to example.png
1Nigar357 Jan 24, 2025
af2d2e0
Delete solutions/advanced/spoj-cot/example1.png
1Nigar357 Jan 24, 2025
5427b2f
Update spoj-cot.mdx (Added a picture)
1Nigar357 Jan 24, 2025
37d8141
Update spoj-cot.mdx
1Nigar357 Jan 24, 2025
95b3933
Update spoj-cot.mdx (added \texttt)
1Nigar357 Jan 28, 2025
8c6ba59
Update spoj-cot.mdx
1Nigar357 Jan 28, 2025
64139d4
Update spoj-cot.mdx
1Nigar357 Jan 28, 2025
d13cdc7
Update spoj-cot.mdx
1Nigar357 Jan 28, 2025
f700aea
Update spoj-cot.mdx
1Nigar357 Jan 28, 2025
c60fda1
Update spoj-cot.mdx
1Nigar357 Jan 29, 2025
c65e0ea
Update spoj-cot.mdx
1Nigar357 Jan 29, 2025
a956b3b
Update spoj-cot.mdx ("Wrapped the elements of formula inside "\texttt"")
1Nigar357 Jan 29, 2025
34e2eee
Update spoj-cot.mdx (Modified the code)
1Nigar357 Feb 1, 2025
122943a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 1, 2025
336dc27
Update spoj-cot.mdx (Decreased the number of nodes)
1Nigar357 Feb 1, 2025
05e5cf9
Update spoj-cot.mdx (updated the solution code)
1Nigar357 Feb 1, 2025
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
2 changes: 1 addition & 1 deletion content/6_Advanced/Persistent.problems.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"isStarred": true,
"tags": ["Persistent Segtree"],
"solutionMetadata": {
"kind": "none"
"kind": "internal"
}
},
{
Expand Down
154 changes: 154 additions & 0 deletions solutions/advanced/spoj-cot.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
id: spoj-cot
source: SPOJ
title: Count on a tree
author: Nigar Hajiyeva
---
## Explanation
1Nigar357 marked this conversation as resolved.
Show resolved Hide resolved

In this code, a persistent segment tree is built for each node in the tree by modifying that of its parent.
1Nigar357 marked this conversation as resolved.
Show resolved Hide resolved
It takes the persistent segment tree of the parent node, adds the value of the current node by adding nodes to the persistent segment tree of the parent node, and stores the root node of the new tree. This way we don't need to build a completely new tree and can just modify the nodes of the tree that require a change.

Each persistent segment tree keeps the frequency of numbers on the way from the root of the tree given in the input to itself. When asked for the kth minimum node in the path of u to v, the code first determines the lowest common ancestor (LCA) of the tree in the input. Next, we run a binary search using the frequency values from the persistent segment tree of four nodes: u, v, the LCA, and the parent of the LCA.

Since the values that are not on the path of u and v, but on the path from the root node to the nodes u and v are also counted in their persistent segment trees, just considering their persistent segment trees would produce wrong results.

**Handling Overcounting with LCA**

We subtract the frequencies of nodes from the root node to the LCA and its parent because we count the nodes twice from the root node to the LCA. In addition, since the LCA is on the path of u to v, we subtract the frequencies from the LCA and the parent of the LCA to consider the value of the LCA.

$$
\texttt{leftCountFromUToV} = \texttt{leftCount}[\texttt{u}]+\texttt{leftCount}[\texttt{v}]−\texttt{leftCount}[\texttt{LCA}]−\texttt{leftCount}[\texttt{parentOfLCA}]
$$

![example](spoj-cot/example.png)

Binary lifting is used to find the lowest common ancestor (LCA) of the two nodes to subtract the frequencies of numbers that lie from the root node to the lowest common ancestor. This is used to accurately maintain the count of appearances of numbers on the path from u to v.

**Note:**
Value compression is used to effectively handle large numbers, which could otherwise cause problems with indexing.
Comment on lines +28 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrap in an tag or something, you can find examples by ctrl-f ing the reop

Copy link
Contributor Author

@1Nigar357 1Nigar357 Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I use Spoiler tag and give it a title note
or can I use $ \ textb f { } $
@SansPapyrus683


## Implementation
1Nigar357 marked this conversation as resolved.
Show resolved Hide resolved

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

<LanguageSection>
<CPPSection>

```cpp


#include <bits/stdc++.h>
using namespace std;
const int MAX = 2e5 + 10, MAX_NODES = 7e6, LOG = 20;
int L[MAX_NODES + 1], R[MAX_NODES + 1], t[MAX_NODES + 1], roots[MAX + 1],
up[MAX + 1][LOG + 1];
int tin[MAX + 1], tout[MAX + 1], val[MAX + 1];
vector<int> graph[MAX + 1];
int nxt = 1, timer = 0;

int update(int v, int l, int r, int pos) {
int nw = ++nxt;

if (l == r) {
t[nw] = t[v] + 1;
return nw;
}

L[nw] = L[v];
R[nw] = R[v];
int m = (l + r) / 2;

if (pos <= m) {
L[nw] = update(L[v], l, m, pos);
} else {
R[nw] = update(R[v], m + 1, r, pos);
}

t[nw] = t[L[nw]] + t[R[nw]];
return nw;
}

void dfs(int from, int p) {
int root = roots[p];
root = update(root, 1, MAX, val[from]);

roots[from] = root;
tin[from] = ++timer;

if (from != 1) {
up[from][0] = p;
} else {
up[from][0] = from;
}

for (int i = 1; i < LOG; i++) { up[from][i] = up[up[from][i - 1]][i - 1]; }

for (int to : graph[from]) {
if (to == p) continue;
dfs(to, from);
}
tout[from] = timer;
}

bool is_ancestor(int u, int v) { return tin[u] <= tin[v] && tout[u] >= tout[v]; }

int lca(int u, int v) {
if (is_ancestor(u, v)) return u;
if (is_ancestor(v, u)) return v;
for (int i = LOG - 1; i >= 0; i--) {
if (is_ancestor(up[u][i], v)) continue;
u = up[u][i];
}
return up[u][0];
}

int query(int a, int b, int anc, int pr, int l, int r, int k) {
if (l == r) { return l; }
int m = (l + r) / 2;
int cnt = t[L[a]] + t[L[b]] - t[L[anc]] - t[L[pr]];
if (cnt >= k) return query(L[a], L[b], L[anc], L[pr], l, m, k);
else return query(R[a], R[b], R[anc], R[pr], m + 1, r, k - cnt);
}

int main() {
int n, m;
cin >> n >> m;
vector<int> compr;
compr.push_back(-INT_MAX);

for (int i = 1; i <= n; i++) {
1Nigar357 marked this conversation as resolved.
Show resolved Hide resolved
cin >> val[i];
compr.push_back(val[i]);
}

sort(compr.begin(), compr.end());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im curious, is there a specific reason ur code coordinate compresses? ig it is faster, but it isn't needed for this problem

as a side note, you definitely do not need 7e6 nodes. 5e6 sufficed for me, and my range was from 0 to INT_MAX

Copy link
Contributor Author

@1Nigar357 1Nigar357 Feb 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right. To avoid segmentation fault I set the number of nodes to 7e6. I changed the number of nodes to 5e6.

I used coordinate compression because the segment tree is a frequency tree and the constraint of the values isn't given in the problem statement. I used this method to ensure that there won't be a problem when building the segment tree. It also speeds up the program

I wrote the coed without the compression and it exceeded the time limit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk my code was perfectly fine without coord compression lol, but ig its fine the way it is

(also, i think maybe even 2e6 or 3e6 would work because ur coord compressing)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, 2e6 works

compr.resize(unique(compr.begin(), compr.end()) - compr.begin());

for (int i = 1; i <= n; i++) {
val[i] = lower_bound(compr.begin(), compr.end(), val[i]) - compr.begin();
}

for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
graph[a].push_back(b);
graph[b].push_back(a);
}

roots[0] = ++timer;
dfs(1, 0);

for (int i = 0; i < m; i++) {
int a, b, k;
cin >> a >> b >> k;
int common = lca(a, b);
int pr = (common == 1) ? 0 : up[common][0];
cout << compr[query(roots[a], roots[b], roots[common], roots[pr], 1, MAX, k)]
<< '\n';
}
}
```

</CPPSection>
</LanguageSection>
Binary file added solutions/advanced/spoj-cot/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading