zoukankan      html  css  js  c++  java
  • LCT

    LCT(Link-Cut Tree),是一类最常见的用来解决动态树问题的工具。所谓动态树问题,大概就是给定一棵树,对其形态进行改变,并进行查询操作。LCT 通过将树划分成不同的链,并使用 Splay 来维护每一条链来解决这一类问题。我们通过一道板子题来说明。

    例题 动态树问题 给出一棵树,每个节点有初始权值。进行四种操作:

    • 查询 x 到 y 路径上的异或和,保证连通
    • 连接 x 和 y,如果已经联通则忽略
    • 断开 x 和 y 中间的边,保证相邻,不保证存在
    • 将点 x 上的权值改成 y

    分析 这是非常经典的 LCT 模板题。LCT将树划分成为不相交的链并使用 Splay 对每条链进行维护。最重要的是换根操作和 Access 操作。具体来说:我们按照每个节点的深度作为权值来构建 Splay。对于一棵 Splay 树中的节点:如果它是根节点,它的父亲就是原树上所在链的最上面一个节点的父亲(链父亲);否则它的父亲就是 Splay 中的父亲。如果它在 Splay 树上没有儿子,它的儿子就是 0。可能有多个不同节点的父亲是同一个,但是每个父亲的儿子(如果存在)只有一个,这被形象地称为,认父不认子

    Access 操作中,每次传入一个节点 u,将 u 和根节点放到一条链当中。而对于不在最短路径上的节点都不属于这条链。具体来说:从 u 开始,先把所有换很的标记从根节点到 u 全部下传(注意顺序),然后将 u splay 到 Splay 树的根节点,并断开 u 的右儿子,那么此时就没有比它更深的节点和它在同一条链上了。然后把再考虑它的父亲(因为是根节点所以是链父亲),把父亲 splay 到对应的 Splay 树的根节点,然后把父亲的右儿子置为它,也即把它所在的链和父亲所在的链合并起来。再对父亲进行同样的操作,直到到达根节点。

    说了那么多,代码其实很简单。

    void Access(int u) {
    	int pos = u, son = 0;
    	while(pos) {
    		Splay(pos), s[pos].ch[1] = son;
    		PushUp(pos), pos = s[son = pos].fa;
    	}
    }
    

    然后就是换根操作。注意到换根对相对位置的直接影响只有 u 到根节点的链,而其他链的 Splay 树都是不变的。所以用区间翻转的操作,直接将 u 到根节点的路径打通,然后把 u splay 到这条链的根,那么此时 u 一定不存在右儿子(因为它就是最深的了),那么在 u 上打一个交换左右子树的标记,那么 u 就没有了左子树,成为了这条链最浅的,那么它就成为了根。

    这个代码只有一行

    inline void MakeRoot(int u) { Access(u), Splay(u), s[u].tag ^= 1, PushDown(u); }
    

    那么所有操作都很显然了,但是还是要注意普通 Splay 和 LCT 上 Splay 的不同,重点在于根节点的父亲不再没有意义,所以需要额外考察一个节点是不是根。

    #include<algorithm>
    #include<cstdio>
    
    const int maxn = 1E+5 + 5;
    const int INF = 0x3f3f3f3f;
    
    int n, m;
    struct SplayNode {
    	int Xor, val, tag;
    	int fa, ch[2];
    } s[maxn];
    
    inline bool nRoot(int u) { return s[s[u].fa].ch[0] == u || s[s[u].fa].ch[1] == u; }
    inline bool Which(int u) { return s[s[u].fa].ch[1] == u; }
    inline void PushUp(int u) { s[u].Xor = s[s[u].ch[0]].Xor ^ s[s[u].ch[1]].Xor ^ s[u].val; }
    inline void PushDown(int u) {
    	if(s[u].tag) {
    		s[s[u].ch[0]].tag ^= 1, s[s[u].ch[1]].tag ^= 1;
    		std::swap(s[u].ch[0], s[u].ch[1]), s[u].tag = 0;
    	}
    }
    
    void Rotate(int u) {
    	bool Whi = Which(u), WhiFa = Which(s[u].fa);
    	int fa = s[u].fa, grand = s[fa].fa, son = s[u].ch[Whi ^ 1];
    	
    	PushDown(fa), PushDown(u);
    	
    	if(nRoot(fa)) s[grand].ch[WhiFa] = u;
    	if(son) s[son].fa = fa;
    	s[u].fa = grand, s[fa].ch[Whi] = son;
    	s[fa].fa = u, s[u].ch[Whi ^ 1] = fa;
    	
    	PushUp(fa), PushUp(u);
    }
    
    int top, sta[maxn];
    void Splay(int u) {
    	int pos = u; top = 0;
    	while(sta[++top] = pos, nRoot(pos)) pos = s[pos].fa;
    	while(top) PushDown(sta[top--]);
    	
    	while(nRoot(u)) {
    		int fa = s[u].fa, grand = s[fa].fa;
    		
    		if(nRoot(fa)) Rotate(Which(u) ^ Which(fa) ? u : fa);
    		Rotate(u);
    	}
    }
    
    void Access(int u) {
    	int pos = u, son = 0;
    	while(pos) {
    		Splay(pos), s[pos].ch[1] = son;
    		PushUp(pos), pos = s[son = pos].fa;
    	}
    }
    
    inline void MakeRoot(int u) { Access(u), Splay(u), s[u].tag ^= 1, PushDown(u); }
    int FindRoot(int u) {
    	Access(u), Splay(u);
    	while(s[u].ch[0]) PushDown(u), u = s[u].ch[0];
    	Splay(u);
    	
    	return u;
    }
    
    inline void Split(int u, int v) { MakeRoot(u), Access(v), Splay(v); }
    inline void Link(int u, int v) {
    	MakeRoot(u);
    	if(FindRoot(v) != u)
    		s[u].fa = v;
    }
    inline void Cut(int u, int v) {
    	MakeRoot(u);
    	if(FindRoot(v) == u && s[v].fa == u && !s[v].ch[0])
    		s[v].fa = s[u].ch[1] = 0, PushUp(u);
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; ++i)
    		scanf("%d", &s[i].val);
    	
    	int opt, x, y;
    	while(m --> 0) {
    		scanf("%d%d%d", &opt, &x, &y);
    		
    		if(opt == 0) Split(x, y), printf("%d
    ", s[y].Xor);
    		if(opt == 1) Link(x, y);
    		if(opt == 2) Cut(x, y);
    		if(opt == 3) Splay(x), s[x].val = y, PushUp(x);
    	}
    }
    
  • 相关阅读:
    FastReport合并多份报表为一份预览打印
    Delphi使用AcroPDF ActiveX显示PDF文件
    Delphi使用Zxing创建二维码
    TreeView和ListView数据库查询数据联动操作
    根据数据库查询结果动态创建控件(仿看板模式显示)
    GridView控件使用
    LayoutControl控件使用
    TreeListLookUpEdit控件使用
    Devexpress TreeList控件使用
    Asp.Net 5上传文件 (Core API方式)
  • 原文地址:https://www.cnblogs.com/whx1003/p/13573105.html
Copyright © 2011-2022 走看看