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

Link Cut Tree #124

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
88 changes: 88 additions & 0 deletions content/graph/LCT.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Author:
* Description: link-cut Tree. Supports BST-like augmentations. (Can be used in place of HLD).
* Current implementation supports update value at a node, and query max on a path.
* For details about the structure, refer to https://en.wikipedia.org/wiki/Link/cut_tree
* Tested on: http://acm.timus.ru/problem.aspx?num=1553
* Status: Passes existing fuzz tests (with function names modified).
Copy link
Member

Choose a reason for hiding this comment

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

We'll want to remove the old link-cut tree, so feel free to do that and modify the fuzz-test. Will also need to update the test to verify that max works (with max replaced by some non-commutative, non-associative function)

Copy link
Author

Choose a reason for hiding this comment

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

Updated the fuzz-test to verify max for now. Will update it later for any new function that we decide on...

*/
struct Node {
bool flip = 0;
// pp = path parent, p = splay tree parent
Node *pp, *p, *c[2];
Copy link
Member

Choose a reason for hiding this comment

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

is it possible to add a one-line comment explaining what p and pp are? or are they standard names?

Copy link
Author

Choose a reason for hiding this comment

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

p = parent, pp = path-parent (and c = children).
p and c are straight forward. path-parent is from the Wikipedia article on LCTs and also this set of lecture notes https://courses.csail.mit.edu/6.851/spring12/scribe/L19.pdf (which I think the Wikipedia page is based on). Besides writing out the acronyms, do you think it's a good idea to explain what a path-parent is? I think that if you understand how LCTs work (by decomposing a tree into preferred paths), then knowing that pp stands for path-pointer should be enough information.

Copy link
Member

Choose a reason for hiding this comment

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

So something like // p ~ parent in segtree, pp = path parent? (Now, I'm also not sure whether that's helpful information when augmenting... but I could imagine such a comment being helpful for understanding)

// add stuff
int val = 0, cval = 0;
Node() { pp = p = c[0] = c[1] = 0; }
void push() {
if (flip) {
rep(i, 0, 2) if (c[i]) c[i]->flip ^= 1;
swap(c[0], c[1]); flip = 0;
}
}
void pull() {
push(), cval = val;
if(c[0]) c[0]->push(), cval = max(cval, c[0]->cval);
if(c[1]) c[1]->push(), cval = max(cval, c[1]->cval);
}
void rot(bool t) {
Node *y = p, *z = y->p, *&w = c[t];
if (z) z->c[z->c[1] == y] = this;
if (w) w->p = y;
y->c[!t] = w;
w = y; p = z;
y->p = this; y->pull();
}
void g() { if (p) p->g(), pp = p->pp; push(); }
void splay() {
g();
while (p) {
Node* y = p; Node *z = y->p;
bool t1 = (y->c[1] != this);
bool t2 = z && (z->c[1] != y) == t1;
if (t2) y->rot(t1);
rot(t1);
if (z && !t2) rot(!t1);
}
pull();
}
Node* access() {
for (Node *y = 0, *z = this; z; y = z, z = z->pp) {
z->splay();
if (z->c[1]) z->c[1]->pp = z, z->c[1]->p = 0;
if (y) y->p = z;
z->c[1] = y; z->pull();
}
splay();
flip ^= 1;
return this;
}
};
struct LinkCut {
vector<Node> nodes;
LinkCut(int N) : nodes(N) {}
bool cut(int u, int v) { /// start-hash
Node *y = nodes[v].access();
Node *x = nodes[u].access();
if (x->c[0] != y || y->c[1]) return false;
x->c[0] = y->p = y->pp = 0;
x->pull();
return true;
} /// end-hash
bool isConnected(int u, int v) {
Node *x = nodes[u].access();
Node *y = nodes[v].access();
return x == y || x -> p;
}
Copy link
Member

Choose a reason for hiding this comment

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

put this function on one line

Copy link
Author

Choose a reason for hiding this comment

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

Changed, but goes slightly over the line limit now.

Copy link
Member

Choose a reason for hiding this comment

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

Hrm, that's no good then... Either revert or change the function names

Copy link
Author

Choose a reason for hiding this comment

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

reverted

bool link(int u, int v) {
if (isConnected(u, v)) return false;
nodes[u].access()->pp = &nodes[v];
return true;
}
void update(int u, int c) {
nodes[u].access()->val += c;
}
int query(int u, int v) { // Find max on the path.
nodes[v].access();
return nodes[u].access()->cval;
}
};
49 changes: 45 additions & 4 deletions fuzz-tests/data-structures/LinkCutTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@ typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;

#include "../../content/graph/LinkCutTree.h"
#include "../../content/graph/LCT.h"
#include "../../content/data-structures/UnionFind.h"

bool dfs(vi &path, bitset<20> &vis, vector<set<int>> &g, int a, int &b) {
path.push_back(a);
vis[a] = 1;
if(a == b) return true;
trav(u, g[a]) if(!vis[u]) if (dfs(path, vis, g, u, b)) return true;
path.pop_back();
return false;
}

int main() {
srand(2);
LinkCut lczero(0);
Expand All @@ -21,18 +30,26 @@ int main() {
LinkCut lc(N);
UF uf(N);
vector<pii> edges;
vector<set<int>> g(N);
vector<int> val(N);
bitset<20> vis;
queue<int> q;
vi cc;
vi path;
rep(it2,0,1000) {
int v = (rand() >> 4) & 3;
int v = (rand() >> 4) & 7;
if (v == 0 && !edges.empty()) { // remove
int r = (rand() >> 4) % sz(edges);
pii ed = edges[r];
swap(edges[r], edges.back());
edges.pop_back();
g[ed.first].erase(ed.second);
g[ed.second].erase(ed.first);
if (rand() & 16)
lc.cut(ed.first, ed.second);
else
lc.cut(ed.second, ed.first);
} else {
} else if(v & 1) {
int a = (rand() >> 4) % N;
int b = (rand() >> 4) % N;
uf.e.assign(N, -1);
Expand All @@ -41,9 +58,33 @@ int main() {
if (!c && v != 1) {
lc.link(a, b);
edges.emplace_back(a, b);
g[a].emplace(b); g[b].emplace(a);
} else {
assert(lc.connected(a, b) == c);
assert(lc.isConnected(a, b) == c);
}
} else if (v & 2) {
int a = (rand() >> 4) % N;
int b = rand() % 10000;
val[a] += b;
lc.update(a, b);
} else {
int a = (rand() >> 4) % N;
vis.reset(), queue<int>().swap(q), vi().swap(cc);
q.push(a);
while(sz(q)) {
int u = q.front(); q.pop();
vis[u] = 1;
cc.push_back(u);
trav(e, g[u]) if(!vis[e]) q.push(e);
}
int r = (rand() >> 4) % sz(cc);
int b = cc[r];
vis.reset();
vi().swap(path);
assert(dfs(path, vis, g, a, b));
int mx = 0;
trav(x, path) mx = max(mx, val[x]);
assert(lc.query(a, b) == mx);
}
}
}
Expand Down