diff --git a/content/graph/LCT.h b/content/graph/LCT.h new file mode 100644 index 000000000..0c8c27728 --- /dev/null +++ b/content/graph/LCT.h @@ -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). + */ +struct Node { + bool flip = 0; + // pp = path parent, p = splay tree parent + Node *pp, *p, *c[2]; + // 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 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; + } + 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; + } +}; diff --git a/fuzz-tests/data-structures/LinkCutTree.cpp b/fuzz-tests/data-structures/LinkCutTree.cpp index 62a126006..ab4d0e5e4 100644 --- a/fuzz-tests/data-structures/LinkCutTree.cpp +++ b/fuzz-tests/data-structures/LinkCutTree.cpp @@ -10,9 +10,18 @@ typedef long long ll; typedef pair pii; typedef vector 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> &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); @@ -21,18 +30,26 @@ int main() { LinkCut lc(N); UF uf(N); vector edges; + vector> g(N); + vector val(N); + bitset<20> vis; + queue 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); @@ -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().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); } } }