zoukankan      html  css  js  c++  java
  • 动态树Link-cut tree(LCT)总结

    动态树是个好玩的东西

    LCT题集

    预备知识

    Splay

    树链剖分(好像关系并不大)

    先搬dalao博客

    • 什么是LCT?
      动态树是一类要求维护森林的连通性的题的总称,这类问题要求维护某个点到根的某些数据,支持树的切分,合并,以及对子树的某些操作。其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构就是LCT(link-cut tree)。
      LCT的大体思想类似于树链剖分中的轻重链剖分,轻重链剖分是处理出重链来,由于重链的定义和树链剖分是处理静态树所限,重链不会变化,变化的只是重链上的边或点的权值,由于这个性质,我们用线段树来维护树链剖分中的重链,但是LCT解决的是动态树问题(包含静态树),所以需要用更灵活的Splay来维护这里的“重链”

    讲了这么多,其实就是维护森林的树链剖分,每一条链用Splay维护

    LCT的主要性质

    1. 每一个Splay维护的是一条从上到下按在原树中深度严格递增的路径,且中序遍历Splay得到的每个点的深度序列严格递增。
      比如有一棵树,根节点为1(深度1),有两个儿子2,3(深度2),那么Splay有3种构成方式:
      {1−2},{3}
      {1−3},{2}
      {1},{2},{3}(每个集合表示一个Splay)
      而不能把1,2,3同放在一个Splay中(存在深度相同的点)

    2. 每个节点包含且仅包含于一个Splay中

    3. 边分为实边和虚边,实边包含在Splay中,而虚边总是由一棵Splay指向另一个节点(指向该Splay中中序遍历最靠前的点在原树中的父亲)。
      因为性质2,当某点在原树中有多个儿子时,只能向其中一个儿子拉一条实链(只认一个儿子),而其它儿子是不能在这个Splay中的。
      那么为了保持树的形状,我们要让到其它儿子的边变为虚边,由对应儿子所属的Splay的根节点的父亲指向该点,而从该点并不能直接访问该儿子(认父不认子)。

    各种操作

    access(x)

    打通(x)到动态树的根的路径(使路径上的所有点在同一个Splay中)

    void access(int x) {
    	for (int y = 0; x; y = x, x = t[x].fa)
    		splay(x), t[x].ch[1] = y, pushup(x);
    	return ;
    }
    

    makeroot(x)

    使(x)变成动态树的根

    void makeroot(int x) {
    	access(x); splay(x);putrev(x);
    	return ;
    }
    

    findroot(x)

    用来判断连通性(类似并查集)(findroot(x)==findroot(y)表明x,y在同一棵树中)

    inline int findroot(int x) {
    	access(x); splay(x);
    	while (t[x].ch[0]) pushdown(x), x = t[x].ch[0];
    	return x;
    }
    

    split(x,y)

    打通(x)(y)的路径(类似access)

    void split(int x, int y) {
    	makeroot(x), access(y), splay(y);
    	return ;
    }
    

    link(x,y)

    连接(x-y)

    void link(int x, int y) {
    	makeroot(x);
    	if (findroot(y) == x) return ;//已经连通
    	t[x].fa = y;//如果写成t[y].fa = x, y与原先的树就断开,就不会与其连通
    	return ;
    }
    

    cut(x, y)

    断开(x-y)

    void cut(int x, int y) {
    	makeroot(x);
    	if (findroot(y) == x && t[x].fa == y && !t[x].ch[1])//满足它们直接连接才断开
    		t[x].fa = t[y].ch[0] = 0, pushup(y);
    	return ;
    }
    

    例题

    洛谷P3690 【模板】Link Cut Tree (动态树)

    Code

    #include<bits/stdc++.h>
    
    #define LL long long
    #define RG register
    const int N = 300010;
    using namespace std;
    
    inline int gi() {
    	RG int x = 0; RG char c = getchar(); bool f = 0;
    	while (c != '-' && (c < '0' || c > '9')) c = getchar();
    	if (c == '-') c = getchar(), f = 1;
    	while (c >= '0' && c <= '9') x = x*10+c-'0', c = getchar();
    	return f ? -x : x;
    }
    
    struct node {
    	int v, s, fa, ch[2];
    	bool rev;
    }t[N];
    int S[N], top;
    void putrev(int x) {
    	swap(t[x].ch[0], t[x].ch[1]);
    	t[x].rev ^= 1;
    	return ;
    }
    #define pushup(x) (t[x].s = (t[x].v^t[t[x].ch[0]].s^t[t[x].ch[1]].s))
    void pushdown(int x) {
    	if (t[x].rev) {
    		if (t[x].ch[0]) putrev(t[x].ch[0]);
    		if (t[x].ch[1]) putrev(t[x].ch[1]);
    		t[x].rev = 0;
    	}
    	return ;
    }
    #define get(x) (t[t[x].fa].ch[1]==x)
    bool isroot(int x) {
    	return (t[t[x].fa].ch[0] != x) && (t[t[x].fa].ch[1] != x);
    }
    void rotate(int x) {
    	int k = get(x), y = t[x].fa, z = t[y].fa;
    	if (!isroot(y)) t[z].ch[get(y)] = x;
    	t[x].fa = z;
    	t[t[x].ch[k^1]].fa = y, t[y].ch[k] = t[x].ch[k^1];
    	t[y].fa = x, t[x].ch[k^1] = y;
    	pushup(y);
    	return ;
    }
    void splay(int x) {
    	S[top = 1] = x;
    	for (RG int i = x; !isroot(i); i = t[i].fa) S[++top] = t[i].fa;
    	for (RG int i = top; i; i--) pushdown(S[i]);
    	while (!isroot(x)) {
    		int y = t[x].fa;
    		if (!isroot(y))
    			(get(x) ^ get(y)) ? rotate(x) : rotate(y);
    		rotate(x);
    	}
    	pushup(x);
    	return ;
    }
    void access(int x) {
    	for (int y = 0; x; y = x, x = t[x].fa)
    		splay(x), t[x].ch[1] = y, pushup(x);
    	return ;
    }
    void makeroot(int x) {
    	access(x); splay(x);putrev(x);
    	return ;
    }
    inline int findroot(int x) {
    	access(x); splay(x);
    	while (t[x].ch[0]) pushdown(x), x = t[x].ch[0];
    	return x;
    }
    void link(int x, int y) {
    	makeroot(x);
    	if (findroot(y) == x) return ;
    	t[x].fa = y;
    	return ;
    }
    void split(int x, int y) {
    	makeroot(x), access(y), splay(y);
    	return ;
    }
    void cut(int x, int y) {
    	makeroot(x);
    	if (findroot(y) == x && t[x].fa == y && !t[x].ch[1])
    		t[x].fa = t[y].ch[0] = 0, pushup(y);
    	return ;
    }
    
    
    int main() {
    	int n = gi(), T = gi();
    	for (RG int i = 1; i <= n; i++) t[i].v = gi();
    	while (T--) {
    		int op = gi(), x = gi(), y = gi();
    		if (!op) {
    			split(x, y);
    			printf("%d
    ", t[y].s);
    		}
    		else if (op == 1) link(x, y);
    		else if (op == 2) cut(x, y);
    		else {
    			access(x); splay(x); t[x].v = y; pushup(x);
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    react路由组件&&非路由组件
    react函数式组件(非路由组件)实现路由跳转
    react使用antd组件递归实现左侧菜单导航树
    【LeetCode】65. Valid Number
    【LeetCode】66. Plus One (2 solutions)
    【LeetCode】68. Text Justification
    【LeetCode】69. Sqrt(x) (2 solutions)
    【LeetCode】72. Edit Distance
    【LeetCode】73. Set Matrix Zeroes (2 solutions)
    【LeetCode】76. Minimum Window Substring
  • 原文地址:https://www.cnblogs.com/zzy2005/p/10312977.html
Copyright © 2011-2022 走看看