[UOJ 207]共价大爷游长沙
给你一棵\(n\)个点的树,要求你滋磁以下操作(共\(m\)次):
- 对于给定的点对\((x, y)\)和\((u, v)\),删除边\((x, y)\),添加边\((u, v)\)。保证操作时\((u, v)\)存在,并且保证操作后的图还是一棵树。
- 向集合\(S\)中加入一个点对\((u, v)\)。
- 从\(S\)中删除一个点对。
- 给定一条边\((x, y)\)(保证在当前树中存在),求问是否所有\((u, v)\in S\)都满足\(u\)到\(v\)的路径经过了\((x, y)\)。
\(1\leq n\leq 10^5, 1\leq m\leq 300000\)。
[UOJ 195][ZJOI2016]大♂森林
ETT野蛮,LCT文明,,,
换生长结点这个操作非常的麻烦。所以考虑给每个生长操作建立一个虚点,每个实点(就是在树中真实存在的点)向他之前最晚建立的一个虚点认父。
然后虚点初始的时候应该向上一次的那个虚点认父(我们可以近似的认为第一个虚点就是1)。然后我们用类似于扫描线的做法,等到了虚点存在的区间里就把它爸爸改成相应的实点,出去了相应区间之后就再改回来。这样这题就很好做了,我们认为虚点点权为0,实点点权为1,然后查询就好做了。
然后还有一点细节问题……比如说换生长结点的时候如何处理x在某一棵树里不存在的情况。但这个不难处理,因为同一个编号的结点一定分布编号连续的一段树里,所以真实起作用的操作范围可以认定为数据给出的操作范围和x的分布区间的交。
再有一点就是查询的时候……如果直接查询两点间splay的和的话,可能会忽略掉一些本来在原图上该有的实点。所以我们要求两点到根的距离,再用LCA去掉不需要的。
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> #include <vector> #include <utility> const int maxn = 100005; const int maxm = 200005; const int maxs = maxm + maxm; struct Node { Node *fa, *ch[2]; int val, sumv; bool rev; int d() { return ((this == fa -> ch[1]) ? 1 : 0); } void sc(Node *c, int dir) { ch[dir] = c; c -> fa = this; } void maintain() { sumv = ch[0] -> sumv + ch[1] -> sumv + val; } void paint() { rev = !rev; std::swap(ch[0], ch[1]); } void pushdown() { if(rev) { ch[0] -> paint(); ch[1] -> paint(); rev = false; } } }; Node pool[maxs]; Node *nil, *cur; void init_pool() { nil = cur = pool; nil -> val = nil -> sumv = 0; nil -> rev = false; nil -> fa = nil -> ch[0] = nil -> ch[1] = nil; } #define T(x) (pool + (x)) Node *refresh(Node *x, int val = 0) { x -> val = x -> sumv = val; x -> rev = false; x -> fa = x -> ch[0] = x -> ch[1] = nil; return x; } bool is_root(Node *x) { return (x -> fa == nil || (x -> fa -> ch[0] != x && x -> fa -> ch[1] != x)); } void zig(Node *x) { Node *y = x -> fa; int d = x -> d(); if(is_root(y)) { x -> fa = y -> fa; } else { y -> fa -> sc(x, y -> d()); } y -> sc(x -> ch[1 ^ d], d); x -> sc(y, 1 ^ d); y -> maintain(); x -> maintain(); } void splay(Node *x) { while(!is_root(x)) { Node *y = x -> fa; if(!is_root(y)) y -> fa -> pushdown(); y -> pushdown(); x -> pushdown(); if(!is_root(y)) { if((x -> d()) ^ (y -> d())) { zig(x); } else { zig(y); } } zig(x); } // x -> maintain(); } Node *access(Node *x) { Node *nx = x, *y = nil; Node *ct = T(1); while(x != nil) { splay(x); x -> pushdown(); if(x -> fa == nil) ct = x; x -> sc(y, 1); x -> maintain(); y = x; x = x -> fa; } splay(nx); return ct; } Node *evert(Node *x) { access(x); x -> paint(); return x; } void link(Node *x, Node *y) { evert(x); x -> fa = y; } void cut(Node *x) { access(x); x -> ch[0] -> fa = nil; x -> ch[0] = nil; x -> maintain(); } void cut(Node *x, Node *y) { evert(x); access(y); int d = x -> d(); y -> ch[d] = nil; y -> maintain(); x -> fa = nil; } int ans[maxm]; using pii = std::pair<int, int>; int n; pii seg_and(int a, int b, int x, int y) { if(a > x) { std::swap(a, x), std::swap(b, y); } if(b < x) return std::make_pair(n + 1, n + 1); if(b >= y) return std::make_pair(x, y); return std::make_pair(x, b); } int ope[maxm][4]; int seg[maxm][2]; Node *bef[maxm]; std::vector<int> beg[maxn], end[maxn]; std::vector<int> query[maxn]; // #define OUTP // #define LOCAL int main() { #ifdef OUTP freopen("forest1.in", "r", stdin); freopen("out", "w+", stdout); #endif int m; scanf("%d%d", &n, &m); init_pool(); refresh(T(1), 1); int cnt0 = 1, cnt1 = 0, cnt2 = 0; Node *last1 = T(1); seg[1][0] = 1; seg[1][1] = n; for(int i = 1; i <= m; i ++) { scanf("%d%d%d", &ope[i][0], &ope[i][1], &ope[i][2]); if(ope[i][0]) { scanf("%d", &ope[i][3]); } if(ope[i][0] == 0) { int c = ++ cnt0; refresh(T(c), 1); link(last1, T(c)); seg[c][0] = ope[i][1]; seg[c][1] = ope[i][2]; #ifdef LOCAL printf("Node %d : (%d, %d)\n", c, seg[c][0], seg[c][1]); #endif } else if(ope[i][0] == 1) { Node *n1 = T(m + i); refresh(n1); link(n1, bef[i] = last1); int l = ope[i][1], r = ope[i][2], x = ope[i][3]; auto s = seg_and(l, r, seg[x][0], seg[x][1]); l = s.first, r = s.second; #ifdef LOCAL printf("Change : (%d, %d) -> %d\n", l, r, x); #endif beg[l].push_back(i); end[r + 1].push_back(i); last1 = n1; } else { int x = ope[i][1], u = ope[i][2], v = ope[i][3]; query[x].push_back(i); } } for(int i = 1; i <= n; i ++) { for(auto id : end[i]) { Node *n1 = T(m + id); cut(n1, T(ope[id][3])); link(n1, bef[id]); } for(auto id : beg[i]) { Node *n1 = T(m + id); cut(n1, bef[id]); link(n1, T(ope[id][3])); } for(auto id : query[i]) { int u = ope[id][2], v = ope[id][3]; evert(T(1)); access(T(u)); int ret = T(u) -> sumv; Node *lca = access(T(v)); ret += T(v) -> sumv; access(lca); ret -= lca -> sumv; ret -= lca -> sumv - 1; ans[id] = ret - 1; } } for(int i = 1; i <= m; i ++) { if(ope[i][0] == 2) { printf("%d\n", ans[i]); } } return 0; }