最近写了几题LCT,算是复习和拓展吧,原来写的LCT都是静态的……下一步打算把QTREE系列都给写了吧……
LCT可以维护树上的路径,路径也即一个序列,于是就用Splay来维护这个路径序列。LCT支持查询树上两点之间的路径的各种信息(基本上Splay能存的LCT都行),并且可以改变树的形态,复杂度都是均摊O(logN)的。
关于LCT的介绍网上到处都是,论文都有好几篇……LCT的操作大概有下面几个:expose(也有叫access的,但感觉expose要霸气一些……)、query、modify、link、cut、makeRoot。
expose x的作用是提出从x到根的路径,后面的操作中基本上都会用到expose。
query和modify这是对路径上的权值进行修改,expose之后用Splay操作就行了。
makeRoot x即换根。换根影响的节点只有从x到当前根的路径上的节点,只需expose x再splay x,之后把Splay中的x节点打上翻转标记即可。
link x y即在两点之间连边。先makeRoot x,再把x的父节点指向y即可。
cut x y即删去x和y之间的边。要考虑x是y的父亲和y是x的父亲的两种情况,expose x再splay y,如果此时y的父节点为x则说明x是y的父亲,清除y的父节点指针。不然再反着来一遍。
cut操作之所以要这样是有理由的。如果x是y的父亲那么expose x之后提出的就是x到根的路径,上面一定不含y,而且此时x的左右儿子也都不是y。这时候再splay y就可以直接清除标记了。
另外注意,即使x和y没有直接相连,cut x y还是会使x和y不连通。所以假如题目没有保证给定的x和y一定直接相连,那么要先判断。可以用set维护与每个点相连的点,或者expose y再splay y,然后检查y的近根端的最远点(即y的前驱)是否为x;如果不是就反着来一遍。
题目:
BZOJ1036
给定一棵树,每个点有点权。操作有:1、询问两点之间路径上的最大权;2、询问两点之间路径上的权值和,3、修改点权。
裸的LCT。
BZOJ2002
给定一个序列S1~Sn。Si代表如果到了i号位置,那么下一次就到i+Si号位置,如果i+Si>n则跳出了序列。操作有:1、询问从i号位置出发,要跳几次才能跳出序列;2、修改Si。
对于每个Si如果i+Si<=n那么从i+Si点向i点连边,那么询问即求从某点到其根的距离,修改则是改变树的形态。求到根的距离实际上就是求从该点到根的路径上的点的个数(显然……),答案即expose之后左子树的大小+1。
BZOJ2049
给定N个点,一开始没有边。操作有:1、在x和y之间连边;2、摧毁x和y之间的边(保证之前有在x和y之间连边的操作);3、询问x和y是否在一个连通块内。保证任意时刻N个点构成森林。
连边和删边都是形态变换,询问是否连通则可以类似query做。令expose x返回最后一次虚边变为实边时父节点的编号,那么expose x再expose y返回的就是LCA(前提是x和y连通)。如果沿着这个“LCA”一路向右能走到x的话那么x和y连通,否则不连通。
SPOJ QTREE
给定一棵树,每条边有边权。操作有:1、询问两点之间路径上的最大权;2、修改边权。
随便选一个根,将边权赋给边的两个端点中离根较远的点上,然后就是裸LCT了。
SPOJ QTREE2
给定一棵树,每条边有边权。操作有:1、询问两点之间路径的长度;2、询问一点到另一点路径上的第k个点。
询问2即expose提出的链上的第k个点,用Splay的find操作即可。注意判断第k个点在是在x的支链上还是y的支链上。
BZOJ2594
给定一个N个点M条边的无向图,每条边有边权。操作有:1、询问两点之间所有路径中最大边权最小的路径上的最大边权;2、删去一条边。
这题是WC2006撸水管局长的数据加强版。首先明确,操作1询问的最大边权的边一定在整个图的最小生成树上(考虑之前的删边操作)。
接下来就是如何处理删边。如果直接处理,似乎好找要加入哪条边。那么我们按倒序离线处理询问,把删边转换为加边。假设加上边(x,y),权值为w,那么找到LCT上x到y的路径上权值最大的边,如果改边的权值>w,那么删去该边,然后link x y。
不过由于这里要改变树的形态,不太好把边权压到点上,所以可以将每条边视为一个点,那么边e(x,y)在LCT中即x->e->y。把点权设为0就不会影响查询操作。
代码:
只发BZOJ2594的,所有的操作这里面都有……感觉这个模板应该不算慢吧,加了FastIO在BZOJ上跑了11s……
//BZOJ2594; 水管局长STRONG_DATA (WC2006); Link-Cut Tree #include <cstdio> #include <cstdlib> #include <algorithm> #define N 100000 #define M 1000000 #define Q 100000 #define SIZE 33000000 using namespace std; class inedge { public: int a, b, w, num; bool v; inedge() : v(false) {} inedge(int _a, int _b, int _w) : a(_a), b(_b), w(_w), v(false) {} inedge(int _a, int _b, int _w, int _num) : a(_a), b(_b), w(_w), num(_num), v(false) {} }e[M + 1], qt[Q + 1]; class Node { public: Node *l, *r, *f, *p; int m, v, num; bool rev; Node() {} Node(int _v, int _num) : l(NULL), r(NULL), f(NULL), rev(false), v(_v), m(_v), p(this), num(_num) {} }*p[N << 1 | 1]; bool op[Q + 1]; int n, m, q, tot, cnt, x, y, w, ans[Q + 1], f[N + 1]; char inbuf[SIZE], *ip = inbuf; inline int getint() { int r = 0; while (*ip < '0' || *ip > '9') ++ip; while (*ip >= '0' && *ip <= '9') r = r * 10 + *(ip++) - '0'; return r; } inline bool cmp(const inedge &x, const inedge &y) { return x.a == y.a ? x.b < y.b : x.a < y.a; } inline bool cmp2(const inedge &x, const inedge &y) { return x.w < y.w; } int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); } inline bool isRoot(Node *x) { return !(x->f) || (x->f->l != x && x->f->r != x); } inline void push(Node *x) { if (!(x->rev)) return ; x->rev = false; if (x->l) x->l->rev ^= 1; if (x->r) x->r->rev ^= 1; swap(x->l, x->r); } inline void update(Node *x) { x->m = x->v, x->p = x; if (x->l && x->l->m > x->m) x->m = x->l->m, x->p = x->l->p; if (x->r && x->r->m > x->m) x->m = x->r->m, x->p = x->r->p; } inline void zig(Node *x) { Node *y = x->f; Node *z = y->f; push(x); if (z && z->l == y) z->l = x; else if (z && z->r == y) z->r = x; if (x->r) x->r->f = y; y->l = x->r; x->r = y, y->f = x, x->f = z; update(y); } inline void zag(Node *x) { Node *y = x->f; Node *z = y->f; push(x); if (z && z->l == y) z->l = x; else if (z && z->r == y) z->r = x; if (x->l) x->l->f = y; y->r = x->l; x->l = y, y->f = x, x->f = z; update(y); } inline void splay(Node *x) { Node *y, *z; while (!isRoot(x)) { y = x->f; z = y->f; if (z) push(z); push(y); if (isRoot(y)) if (y->l == x) zig(x); else zag(x); else if (z->l == y) if (y->l == x) zig(y), zig(x); else zag(x), zig(x); else if (y->l == x) zig(x), zag(x); else zag(y), zag(x); } push(x); update(x); } inline void expose(Node *x) { for (Node *y = NULL; x; x = x->f) { splay(x); x->r = y; update(y = x); } } inline void makeRoot(Node *x) { expose(x); splay(x); x->rev ^= 1; } inline void cut(Node *x, Node *y) { expose(y); splay(x); if (x->f == y) { x->f = NULL; if (y->l == x) y->l = NULL; else if (y->r == x) y->r = NULL; } else { expose(x); splay(y); y->f = NULL; if (x->l == y) x->l = NULL; else if (x->r == y) x->r = NULL; } } inline void link(Node *x, Node *y) { makeRoot(x); x->f = y; } inline int findedge(int x, int y) { return lower_bound(e + 1, e + m + 1, inedge(x, y, 0), cmp) - e; } inline Node* query(Node *x, Node *y) { Node *r = NULL; expose(y); for (y = NULL; x; x = x->f) { splay(x); if (!(x->f)) { if (x->r && (!r || x->r->m > r->m)) r = x->r->p; if (y && (!r || y->m > r->m)) r = y->p; } x->r = y; update(y = x); } return r; } inline void modify(inedge &c) { Node *x = p[c.a], *y = p[c.b], *r; r = query(x, y); if (r->v <= c.w) return ; cut(r, p[e[r->num].a]); cut(r, p[e[r->num].b]); delete r; r = new Node(c.w, c.num); link(r, x); link(r, y); } int main() { // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); fread(inbuf, sizeof(char), sizeof(char) * SIZE, stdin); // scanf("%d%d%d", &n, &m, &q); n = getint(), m = getint(), q = getint(); for (int i = 1; i <= n; ++i) p[i] = new Node(0, -i), f[i] = i; for (int i = 1; i <= m; ++i) { // scanf("%d%d%d", &x, &y, &w); x = getint(), y = getint(), w = getint(); if (x > y) swap(x, y); e[i] = inedge(x, y, w); } sort(e + 1, e + m + 1, cmp); for (int i = 1; i <= m; ++i) e[i].num = i; for (int i = 1; i <= q; ++i) { // scanf("%d%d%d", &w, &x, &y); w = getint(), x = getint(), y = getint(); if (x > y) swap(x, y); qt[i] = inedge(x, y, 0); op[i] = w == 1; if (w == 1) continue; int pos = findedge(x, y); e[pos].v = true, qt[i].num = pos, qt[i].w = e[pos].w; } tot = m; for (int i = 1; i <= tot; ++i) if (e[i].v) swap(e[i--], e[tot--]); sort(e + 1, e + tot + 1, cmp2); for (int i = 1; i <= tot; ++i) e[i].v = false; cnt = n; for (int i = 1; i <= tot; ++i) if (find(e[i].a) != find(e[i].b)) { p[++cnt] = new Node(e[i].w, e[i].num); link(p[e[i].a], p[cnt]); link(p[cnt], p[e[i].b]); f[find(e[i].a)] = find(e[i].b); e[i].v = true; } sort(e + 1, e + m + 1, cmp); for (int i = q; i > 0; --i) if (op[i]) ans[i] = query(p[qt[i].a], p[qt[i].b])->v; else modify(qt[i]); for (int i = 1; i <= q; ++i) if (op[i]) printf("%d\n", ans[i]); return 0; }