LCT小结

来源:互联网 发布:floyd算法举例 编辑:程序博客网 时间:2024/05/16 16:03

最近写了几题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 33000000using 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);elseif (z->l == y)if (y->l == x) zig(y), zig(x);else zag(x), zig(x);elseif (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;}


原创粉丝点击