From ca890338a067ae38de1efe22b78aab968457b30d Mon Sep 17 00:00:00 2001 From: Daniel Zhu Date: Tue, 20 Aug 2024 22:26:18 -0700 Subject: [PATCH] finish lct --- .gitignore | 2 +- USACO/.DS_Store | Bin 12292 -> 0 bytes impls/lct.cc | 202 +++++++++++++++++++++++++++++++++++++++ manim/splay.py | 246 +++++++++++++++++++++++++++--------------------- run.sh | 4 +- stress.sh | 5 +- 6 files changed, 348 insertions(+), 111 deletions(-) delete mode 100644 USACO/.DS_Store create mode 100644 impls/lct.cc diff --git a/.gitignore b/.gitignore index bfc9914..d8f9084 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ b.out gen test* **/*.in -.DS_Store +**/.DS_Store *.out a.cc b.cc diff --git a/USACO/.DS_Store b/USACO/.DS_Store deleted file mode 100644 index 3cd5336113317791b8d0d7fc77f78f7721301c4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12292 zcmeI2OK2oT7=Wv1CfNybC0UH!%_}nGLEuts} z8#BeqRG89EXJjBtme0Z61|YmO5&H~N?&H~N?&H{hY z0&?$#D{t-hI@dj&1)K%`*A|fLgAcb%OD0{{saiVVAzgSWmoB`z;4{PlD$h4*$)xK# zRYjqUDhjDC>X{J3)5Rg*m+Fv~OuDX9U0is&xbRHNo{3OA)jE{(mAddsI(1KH0cU}< z1s;Gb6noEw<#M$VRv|YZoPT+J{L=PawnptwH0Ebz{2#^dYCfVC9<3}i8)0(>YL!zt zkqjShgj1$%DCBLxeOku;Ci+L3`h+2Bp>3PigWutWdnaFc?xOV$!*gNs4!lOocNnoq zf8LdJgpy{zA*3UibYX)b1&xH1*Jw#PN=Y+d^<8W_N=b7+5YkahdgT&B3K|J1uhD`u z5CuvqVAOfvGrbR*gw#6|AX1QG(hrVR?cP@!2`R5ZNWFKUfq0w-qG*wx-<7mTNHfhQ ztp}6dann?qw1{oWYY@`RCM69hsdvM!qyZrnJ*Nq2fJq;_v6dzUjl`zB1|ij(dNiX0 z(QlE;xzG20KOxP$1=dG>(~n7C`j#ODjf9lfAf#De+B9d*CYd!#oI|5D)+3Z6cM;ct z84n$tj;tA%#e|%k@M(8RmradL4^}3I5A-PCxw)7RWSH;fy$@18B~L8K$tm9^RBh}R z2x~;j^@!!8<1sfQcZ%p8$!OfUFk|zPvB=2@U+y!^XN^O(3SFJ*Ul^~{emzGK9~pzc zemZOOkrBwr37?)RInl3AzUe#>a_ymfMvwBf%UwRM1NZON_4S0$l*QwZlT$vmzw5eF ztGHTm(tP$BF1p{2`K%TE*{>}=YyIZrl&_%sSH4?%e#mu(16i~@H2ySn9FKnx~sCEgGE{XL2BvuAJ}c$FQTKA!A8rYv4%IXSb+ zMtqj{ExjL>ZQhelWfRnf2v(E(}-eUQzQ-SWs5g$1d_-fw? z+kWIkfRj@`AF7V~*E$W5CoC+VeHL&T*MaR|PM)lxX{PY(wCDBN{VP2yJX;E0VRFNu23Yr$_yNDE|m^A2S zP9BLxd5xB&gVd%fX5~o-t1FB1=7jqF>U^WVxKt?Yc$sXjt$j~^PrfsMqB0w=dX?ig+70ve`sy3Bj0o2;B7}H9&XGvL%A2j<#{?4 zdrcZ88)8sf6f)X$W^X5?_w4@vzo@87odui){(~0qa^*@{I#JQ0VZUPrGJLpC!7X(* z;g%PCRz=_;Rpb}-b3CN#=Xglf25V#`ldkI&q;UQ1e+0n4PP+XcdWzrOv7gLqwA@wQ M{vYl6|Iq#a5208XBLDyZ diff --git a/impls/lct.cc b/impls/lct.cc new file mode 100644 index 0000000..9fce0a0 --- /dev/null +++ b/impls/lct.cc @@ -0,0 +1,202 @@ +#include +#include +#include +#define int long long +using namespace std; + +struct F { + int w = 1, b = 0; + int operator()(int x) const { return w * x + b; } + F operator()(F f) const { return F{w * f.w, w * f.b + b}; } + operator bool() const { return w != 1 || b != 0; } +}; + +const int N = 2e5 + 1; +const int inf = 1e18; +struct Node; +typedef struct Node * T; +struct Node { + int v, s, m, M; + int id, sz; + inline static int n = 0; + static Node t[N]; + array c{}; + T p{}; + F f{}; + bool rev = false; + Node() {} + Node(int id, int v) : v(v), s(v), m(v), M(v), id(id), sz(1), rev(false) {} + static T init(int id, int v, T l = nullptr, T r = nullptr) { + t[n] = Node(id, v); + t[n].attach(0, l); + t[n].attach(1, r); + return &t[n++]; + } + static int size(T x) { return x ? x->sz : 0; } + static int sum(T x) { return x ? x->s : 0; } + static int ID(T x) { return x ? x->id : -1; } + static int mn(T x) { return x ? x->m : inf; } + static int mx(T x) { return x ? x->M : -inf; } + T push() { + if (rev) { + swap(c[0], c[1]); + for (T x : c) if (x) x->rev ^= rev; + rev = false; + } + if (f) { + v = f(v); + s = f.w * s + f.b * sz; + for (T x : c) if (x) x->f = f(x->f); + f = F{1, 0}; + } + return this; + } + void pull() { + for (T x : c) if (x) x->push(); + sz = size(c[0]) + size(c[1]) + 1; + s = sum(c[0]) + sum(c[1]) + v; + m = min({mn(c[0]), mn(c[1]), v}); + M = max({mx(c[0]), mx(c[1]), v}); + } + bool side() { return p->c[1] == this; } + bool is_root() { return !p || p->c[side()] != this; } + void attach(int i, T x) { + push(); + if (x) x->p = this; + c[i] = x; + pull(); + } + T detach(int i) { + push(); + T t = c[i]; + if (c[i]) c[i]->p = nullptr; + c[i] = nullptr; + return pull(), t; + } + void rotate() { + T p = this->p, g = p->p; + if (g) g->push(); + p->push(), push(); + int i = side(); + if (!p->is_root()) g->attach(p->side(), this); + else this->p = g; + p->attach(i, c[!i]); + attach(!i, p); + } + T splay() { + for (; !is_root(); rotate()) { + if (!p->is_root()) { + (side() == p->side()) ? p->rotate() : rotate(); + } + } + return this; + } + friend ostream& operator<<(ostream& os, T s) { + if (!s) return os; + os << s->c[0]; + os << s->id << " "; + return os << s->c[1]; + } +}; + +Node Node::t[N]{}; + +struct LCT { + T x[N]{}; // nodes + /* int r = 0; */ + T access(int u, bool lca = false) { + for (T y = x[u], z = nullptr; y; z = y, y = y->p) { + y->splay(); + if (lca && !y->p) return y; + y->attach(1, z); // switch preferred path + } + x[u]->splay(); + return x[u]; + } + void print_chains() { + /* cout << "chains: " << endl; */ + /* for (int i = 1; i <= 10; i++) if (x[i] && x[i]->is_root()) cout << x[i] << endl; */ + /* cout << "----" << endl; */ + } + int root(int u) { // get root of forest containing u + T x = access(u); + while ((x->push())->c[0]) x = x->c[0]; + return x->id; + } + T reroot(int u) { + access(u)->rev ^= true; + return x[u]->push(); + } + int node(int id, int v) { + if (!x[id]) x[id] = Node::init(id, v); + return id; + } + void link(int u, int v) { // add v as child of u + access(u)->attach(1, reroot(v)); + } + int cut(int u) { // cut edge from u to parent + return access(u)->detach(0), u; + } + int query(int u, int v, function f) { + /* int R = root(u); */ + reroot(u); + assert(root(v) == u); + int x = f(access(v)); + /* reroot(R); */ + return x; + } + void upd(int u, int v, function f) { + /* int R = root(u); */ + reroot(u); + assert(root(v) == u); + f(access(v)); + /* reroot(R); */ + } + int lca(int u, int v) { + access(u); + return access(v, true)->id; + } +} lct; + +int a[N]; + +signed main() { + cin.tie(0)->sync_with_stdio(0); + int n, q; cin >> n >> q; + for (int i = 1; i <= n; i++) { + cin >> a[i]; + lct.node(i, a[i]); + } + for (int i = 1; i <= n; i++) assert(lct.x[i]); + for (int i = 1; i < n; i++) { + int u, v; cin >> u >> v; + lct.link(u, v); + } + int r; cin >> r; + lct.reroot(r); + while (q--) { + int k; cin >> k; + if (k == 0) { int x; cin >> x; lct.reroot(x); } + else if (k < 3) { + int x, y, z; cin >> x >> y >> z; + cout << k << " " << x << " " << y << " " << z << endl; + cout << "stuff: " << (k == 2) << endl; + lct.upd(x, y, [=](T t) { t->f = F{k == 2, z}(t->f); }); + } + else if (k < 6) { + int x, y; cin >> x >> y; + cout << k << " " << x << " " << y << endl; + cout << lct.query(x, y, [=](T t) { return k == 3 ? t->m : (k == 4 ? t->M : t->s); }) << endl; + } + else if (k == 6) { + int x, y; cin >> x >> y; + if (lct.lca(x, y) == x) continue; + lct.link(y, lct.cut(x)); + } + else { + int x, y; cin >> x >> y; + cout << lct.lca(x, y) << endl; + } + } +} + diff --git a/manim/splay.py b/manim/splay.py index c73b0a6..e1b5b9d 100644 --- a/manim/splay.py +++ b/manim/splay.py @@ -1,110 +1,146 @@ # %% splay animation from manim import * import networkx as nx +import copy -class Splay(Scene): - def from_str(self, s): - g = [[-1, -1]] - for i in range(len(s)): - if s[i] == 'L': - g[-1][0] = i + 1 - else: # s[i] == 'R' - g[-1][1] = i + 1 - g.append([-1, -1]) - return g - def get_par(self, g): - p = [-1] * len(g) - for (i, [l, r]) in enumerate(g): - if l >= 0: p[l] = i - if r >= 0: p[r] = i - return p - def build_graph(self, g, root, x): - p = [0] * len(g) - G = nx.Graph() - lt = {} - def dfs(u, d): - nonlocal x - nonlocal G - G.add_node(u); - if g[u][0] >= 0: - G.add_edge(u, g[u][0]) - dfs(g[u][0], d - 1) - p[u] = x - lt[u] = [x, d, 0] - x += 1 - if g[u][1] >= 0: - G.add_edge(u, g[u][1]) - dfs(g[u][1], d - 1) - dfs(root, 2) - return Graph(list(G.nodes), list(G.edges), layout=lt, labels=True), g, lt, G - def animate_between(self, G: Graph, GG: Graph): - l = [] - print(G._graph.nodes) - for x in G._graph.nodes: - assert(x in GG._graph.nodes) - l.append(G[x].animate.move_to(GG._layout[x])) - for e in G._graph.edges: - if e not in GG._graph.edges: - l.append(G.animate.remove_edges(e)) - print(len(l)) - self.play(l) - print(G._layout[1]) - ll = [] - for e in GG._graph.edges: - if e not in G._graph.edges: - ll.append(G.animate.add_edges(e)) - return ll - def rotate(self, G: Graph, g, x, l): - p = self.get_par(g) - y = p[x] - z = p[y] - print("original: ", g, x, y, z) - if z >= 0: - if g[z][0] == y: - g[z][0] = x - else: - g[z][1] = x - if g[y][0] == x: - g[y][0] = g[x][1] - g[x][1] = y - else: - g[y][1] = g[x][0] - g[x][0] = y - p = self.get_par(g) - root = 0 - for i in range(len(p)): - if p[i] == -1: root = i - GG, gg, lt, GGG = self.build_graph(g, root, l) - print(g, GG._graph.nodes, GG._layout) - # self.play(G.animate.update_edges(GG)) - # self.play(G.animate.add_edges((y, x), edge_config={"color": RED})) - l = self.animate_between(G, GG) - # l += [G.animate.add_edges((y, x))] - if len(l) > 0: self.play(l) - return g - def splay(self, G: Graph, g, x, l): - self.play(G[x].animate.set_color(RED)) - p = self.get_par(g) - root = 0 - for i in range(len(p)): - if p[i] == -1: root = i - while p[x] >= 0: - y = p[x] - z = p[y] - if z >= 0: - if (g[z][0] == y) == (g[y][0] == x): - g = self.rotate(G, g, y, l) + +class Splay(VMobject): + def __init__(self, g, **kwargs): + super().__init__(**kwargs) + # store an internal adj list specific to maintain binary tree structure + if isinstance(g, str): + self.g = self.from_str(g) + else: + self.g = g + self.G = self.build_graph() + self.add(self.G) + self.G.generate_target(False) + + @staticmethod + def from_str(s): + g = [[-1, -1]] + for i in range(len(s)): + if s[i] == "L": + g[-1][0] = i + 1 + else: # s[i] == 'R' + g[-1][1] = i + 1 + g.append([-1, -1]) + return g + + # gets array of parents for this graph + def get_all_parents(self, g): + p = [-1] * len(g) + for i, [l, r] in enumerate(g): + if l >= 0: + p[l] = i + if r >= 0: + p[r] = i + return p + + def rebuild(self): + layout = {} + G = nx.Graph() + + def dfs(u, d): + nonlocal layout + if self.g[u][0] >= 0: + G.add_edge(u, self.g[u][0]) + dfs(self.g[u][0], d - 1) + x, _, z = self.G[u].get_center() + layout[u] = [x, d, z] + if self.g[u][1] >= 0: + G.add_edge(u, self.g[u][1]) + dfs(self.g[u][1], d - 1) + + dfs(self.root(), 2) + return Graph(list(G.nodes), list(G.edges), layout=layout, labels=True) + + def build_graph(self): + g = self.g + G = nx.Graph() + lt = {} + + x = 0 + + def dfs(u, d): + nonlocal x + nonlocal G + G.add_node(u) + if g[u][0] >= 0: + G.add_edge(u, g[u][0]) + dfs(g[u][0], d - 1) + lt[u] = [x, d, 0] + x += 1 + if g[u][1] >= 0: + G.add_edge(u, g[u][1]) + dfs(g[u][1], d - 1) + + dfs(0, 2) + return Graph(list(G.nodes), list(G.edges), layout=lt, labels=True) + + # animates current graph into graph G + def animate_to(self, G: Graph): + animations1 = [] # store in a list so we can play them all in parallel + animations1 += [ + self.G[x].animate.move_to(G._layout[x]) for x in self.G._graph.nodes + ] + to_remove = [e for e in self.G.target._graph.edges if not G._graph.has_edge(*e)] + print(to_remove) + animations1 += [self.G.animate.remove_edges(*to_remove)] + print(self.G, self.G.target) + to_add = [e for e in G._graph.edges if not self.G.target._graph.has_edge(*e)] + print(to_add) + animations2 = self.G.animate.add_edges(*to_add) + print(self.G, self.G.target) + print(self.G.target.edges) + + return Succession(AnimationGroup(*animations1), animations2) + + def root(self): + p = self.get_all_parents(self.g) + root = 0 + for i in range(len(p)): + if p[i] == -1: + root = i + return root + + def splay_rotate(self, x): + p = self.get_all_parents(self.g) + y = p[x] + z = p[y] + if z >= 0: + if self.g[z][0] == y: + self.g[z][0] = x + else: + self.g[z][1] = x + if self.g[y][0] == x: + self.g[y][0] = self.g[x][1] + self.g[x][1] = y else: - g = self.rotate(G, g, x, l) - g = self.rotate(G, g, x, l) - p = self.get_par(g) - def construct(self): - G, g, _, _ = self.build_graph(self.from_str("LRLR"), 0, -5) - G1, g1, _, _ = self.build_graph(self.from_str("LRLLR"), 0, 1) - self.add(G) - self.add(G1) - self.play(Write(Text('Zig-zag').next_to(G, UP)), Write(Text('Zig-zig').next_to(G1, UP))) - self.splay(G, g, 4, -5) - self.splay(G1, g1, 5, 1) - # g = self.rotate(G, g, 2) - # self.rotate(G, g, 2) + self.g[y][1] = self.g[x][0] + self.g[x][0] = y + return self.animate_to(self.rebuild()) + + def splay(self, x): + animations = [] + animations.append(self.G[x].animate.set_color(RED)) + while self.root() != x: + p = self.get_all_parents(self.g) + y = p[x] + z = p[y] + if y >= 0: + animations.append(self.splay_rotate(x)) + # if (self.g[z][0] == y) == (self.g[y][0] == x): + # animations.append(self.splay_rotate(y)) + # else: + # animations.append(self.splay_rotate(x)) + print(len(animations)) + return Succession(*animations) + + +class SplayAnimation(Scene): + def construct(self): + splay = Splay("LRLR") + self.add(splay) + self.wait() + self.play(splay.splay(4)) diff --git a/run.sh b/run.sh index 203568d..eb916df 100755 --- a/run.sh +++ b/run.sh @@ -1,8 +1,8 @@ start=$SECONDS file=$(find -E . -regex '.*\.(cc|cpp)' | xargs ls -t | head -1) echo "compiling $file" -g++ -std=c++17 -O2 -Wall $file -g -fsanitize=address -fsanitize=undefined || exit 1 +g++ -std=c++17 -O2 -Wall $file -g || exit 1 echo "compiled $file in $((SECONDS - start)) seconds" start=$SECONDS -./a.outtest.in - ./a a.out - ./b b.out + ./a a.out || break + ./b b.out || break diff -w a.out b.out || break done -