zoukankan      html  css  js  c++  java
  • Link-Cut-Tree学习笔记

    Link-Cut-Tree学习笔记

    原理:将树分成一条条链,之后用Splay维护

    一.定义数组

    int son[MAXN][2], fa[MAXN], sum[MAXN], val[MAXN], sta[MAXN], lazy[MAXN];
    

    son记录每个节点的两个重儿子

    fa记录每个节点的父亲

    (fa和son双向为重边,单向为轻边)

    sum为本题特有的数组,含义为子树异或和,val为节点权值,lazy为翻转标记

    二.基础操作

    1.merge
    void merge(int x){
    	sum[x] = sum[son[x][0]] ^ sum[son[x][1]] ^ val[x];
    }
    

    每当节点更新时要用一遍merge

    2.spread
    void spread(int x){
    	if(lazy[x]){
            lazy[son[x][0]] ^= 1;
            lazy[son[x][1]] ^= 1;
            lazy[x] ^= 1, swap(son[x][0], son[x][1]);
    	}
    }
    

    传标记的函数

    3.isroot
    bool isroot(int x){
    	return son[fa[x]][0] != x && son[fa[x]][1] != x;
    }
    

    判断一个点是否为当前Splay块里的根

    4.rotate
    void rotate(int x){
    	int y = fa[x], z = fa[y], id = son[y][1] == x;
    	if(!isroot(y)) son[z][son[z][1]==y] = x;//和普通Splay不一样的地方,因为Splay的目的是旋转到当前Splay块的顶部
    	fa[son[x][id^1]] = y, son[y][id] = son[x][id^1];
    	fa[y] = x, son[x][id^1] = y, fa[x] = z;
    	merge(y), merge(x);//注意不要先merge(x)了
    }
    
    5.splay
    void splay(int x){
        //因为要保证lazy的传递,所以需要提前将这一条链上的点全部spread
    	sta[0] = 0, sta[++sta[0]] = x;
    	for(int i=x; !isroot(i); i=fa[i]) sta[++sta[0]] = fa[i];
    	for(int i=sta[0]; i>=1; i--) spread(sta[i]);
    	while(!isroot(x)){
    		int y = fa[x], z = fa[y];
    		if(!isroot(y)) (son[z][0] == y) ^ (son[y][0] == x) ? rotate(x) : rotate(y);
    		rotate(x);
    	}
    }
    

    三.普通操作

    1.access
    void access(int x){
    	for(int y=0; x; y=x, x=fa[x])
            splay(x), son[x][1] = y, merge(x);
    }
    

    打通原树根到x的唯一路径(不多也不少)

    2.find
    int find(int x){
    	access(x), splay(x);
    	while(son[x][0]) x = son[x][0];
    	return x;
    }
    

    找这个树的根(即打通路径后的Splay块中深度最小的节点)

    3.makeroot
    void makeroot(int x){
    	access(x), splay(x), lazy[x] ^= 1;
    }
    

    换根操作,当splay(x)后,x为深度最大的点,所以无右节点,此时反转序列之后将变成深度最小的点。即新的数根。因为换根操作不会影响其他的Splay块的深度关系,所以x就是新的树根。

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

    打通一条(x o y)的路径,并且打完之后顶部Splay块以y为根

    void link(int x,int y){
    	if(find(x) == find(y)) return;
    	makeroot(x), fa[x] = y;
    }
    

    (x o y)之间连一条边,因为我们指定它连轻边,所以不用更新y的sum值

    这里第3行不能写为

    access(x), splay(x), fa[x] = y;
    

    这等价于x所在Splay的根和y连边,而不是x

    6.cut
    void cut(int x,int y){
    	split(x, y);
    	if(son[y][0] == x && son[x][1] == 0) son[y][0] = fa[x] = 0;
    	merge(y);
    }
    

    第3行保证了x和y连接(即x和y之间不存在其他深度的点)(注意要(son[x][1]==0)不然有可能x的右儿子还会有节点)

    Luogu模板代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    inline long long read(){
    	long long ans = 0, f = 1;
    	char ch = getchar();
    	while(ch < '0' || ch > '9')
    		f *= (ch == '-') ? -1 : 1, ch = getchar();
    	do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar();
    	while(ch >= '0' && ch <= '9');
    	return ans * f;
    }
    
    const int MAXN = 3e5 + 5;
    
    struct LinkCutTree{
    	int son[MAXN][2], fa[MAXN], sum[MAXN], val[MAXN], sta[MAXN], lazy[MAXN];
    	void merge(int x){
    		sum[x] = sum[son[x][0]] ^ sum[son[x][1]] ^ val[x];
    	}
    	void spread(int x){
    		if(lazy[x]) lazy[son[x][0]] ^= 1, lazy[son[x][1]] ^= 1, lazy[x] ^= 1, swap(son[x][0], son[x][1]);
    	}
    	bool isroot(int x){
    		return son[fa[x]][0] != x && son[fa[x]][1] != x;
    	}
    	void rotate(int x){
    		int y = fa[x], z = fa[y], id = son[y][1] == x;
    		if(!isroot(y)) son[z][son[z][1]==y] = x;
    		fa[son[x][id^1]] = y, son[y][id] = son[x][id^1];
    		fa[y] = x, son[x][id^1] = y, fa[x] = z;
    		merge(y), merge(x);
    	}
    	void splay(int x){
    		sta[0] = 0, sta[++sta[0]] = x;
    		for(int i=x; !isroot(i); i=fa[i]) sta[++sta[0]] = fa[i];
    		for(int i=sta[0]; i>=1; i--) spread(sta[i]);
    		while(!isroot(x)){
    			int y = fa[x], z = fa[y];
    			if(!isroot(y)) (son[z][0] == y) ^ (son[y][0] == x) ? rotate(x) : rotate(y);
    			rotate(x);
    		}
    	}
    	void access(int x){
    		for(int y=0; x; y=x, x=fa[x]) splay(x), son[x][1] = y, merge(x);
    	}
    	void makeroot(int x){
    		access(x), splay(x), lazy[x] ^= 1;
    	}
    	int find(int x){
    		access(x), splay(x);
    		while(son[x][0]) x = son[x][0];
    		return x;
    	}
    	void split(int x,int y){
    		makeroot(x), access(y), splay(y);
    	}
    	void cut(int x,int y){
    		split(x, y);
    		if(son[y][0] == x && son[x][1] == 0) son[y][0] = fa[x] = 0;
    		merge(y);
    	}
    	void link(int x,int y){
    		if(find(x) == find(y)) return;
    		makeroot(x), fa[x] = y;
    	}
    }LCT;
    
    int main(){
    	int n = read(), m = read();
    	for(int i=1; i<=n; i++) LCT.val[i] = LCT.sum[i] = read();
    	while(m--){
    		int op = read(), x = read(), y = read();
    		if(op == 0) LCT.split(x, y), printf("%d
    ", LCT.sum[y]);
    		else if(op == 1) LCT.link(x, y);
    		else if(op == 2) LCT.cut(x, y);
    		else if(op == 3) LCT.access(x), LCT.splay(x), LCT.val[x] = y, LCT.merge(x);
    	}
        return 0;
    }
    

    结束了

  • 相关阅读:
    读写分离
    使用xtrabackup对不同版本的mysql进行备份还原
    tomcat开启管理页面
    面试简单整理之rabbitmq
    面试简单整理之zookeeper
    面试简单整理之mybatis
    面试简单整理之spring、spring mvc
    面试简单整理之Redis
    记一次菜鸟的惊心动魄(另求原因和说明)
    面试简单整理之IO
  • 原文地址:https://www.cnblogs.com/PHDHD/p/14127466.html
Copyright © 2011-2022 走看看