• Link-Cut Tree


    前言

    update 2021.2.16 加上了目录,修复了部分 (markdown)

    大佬博客

    这是一篇质量还行的博客

    前排放大佬的博客用来(orz)

    FlashHu orz

    前置芝士

    Splay

    据说( equire{enclose}enclose{horizontalstrike}{ ext{Treap}})也可以,但蒟蒻作者不会

    树链剖分(如果你学过的话可以更好理解,没学过也没有关系)

    定义

    又到了喜闻乐见的百度百科自学时间

    真·定义:

    动态树,( ext{Link-Cut Tree}),简称( ext{LCT})

    其中的( ext{Link})指加边,( ext{Cut})指删边

    类似于树链剖分,但是线段树变成了(Splay)(大概是这样的)

    我们就可以在树上自由地删边、连边了,当然也可以搞其它的

    作用

    • 查询、修改链上的信息(最值,总和等)

    • 换根

    • 动态连边、删边

    • 合并两棵树、分离一棵树

    • 动态维护连通性

    • 敬请期待更多操作

    讲解

    令人窒息的百度百科(反正我再也不会看了)

    如果你一头雾水地回来了,那么我又双叒叕成功了

    ( ext{LCT})是由很多( ext{Splay})维护的一个数据结构

    其中最重要的当然就是( equire{enclose}enclose{horizontalstrike}{ ext{Splay}})

    零、大概的实现

    如果有一棵树,我们将每个点选一个儿子为偏爱儿子(类似于树链剖分的重儿子,但是这是我们自己定的)

    然后偏爱儿子与父亲的边为实边,其余的为虚边

    每一个实边相连的部分为一棵( ext{Splay})

    我们将每一个实边相连的部分(一条链)叫做偏爱路径((Preferred Path))

    组成的( ext{Splay})叫做辅助树((Auxiliary Tree))

    学专业术语就是出去装A_C用的

    假设我们可以构造这么一棵树,长这样:(红色为实边,绿色为虚边)

    其中框出来的部分就是一棵( ext{Splay})

    图好丑啊啊啊啊啊!!!

    注意不要把( ext{F})给忘了

    对于每一棵( ext{Splay}),我们按深度的从小到大排序

    排序完了之后当然就是:前面(左边)的是祖先,后面(右边)的是后代

    ( ext{LCT})不画了,(lazy),但是( ext{LCT})要满足上面的规则

    其中:

    虚边认父不认子(如只记录(F)的父亲是(E),但不记录(E)的儿子是(F))

    实边都要认(两边都要存)

    这些东西是为了保证我们接下来的操作

    一、( ext{access})

    核心操作!!!

    如果你英语好,或者知道这个东西

    你就可以知道这个单词的意思是访问

    具体的,它用来实现把节点(x)与树根连接起来(搞到一棵( ext{Splay})里)

    跟访问有什么关系呢?

    我们举个栗子,把上面的树搬下来:

    假设我们要( ext{access(L)}),就要把(A)(L)路径上的所有边全部变为实边

    怎么实现呢?

    1.首先我么要把(L)转为自己所在的( ext{Splay})的根

    注意我画的图不是按照排序规律画的(因为懒)

    现在(K)(L)的左边

    2.然后,由于我们马上就要把(L)(E)连成实边了,所以我们要把(L)的右儿子赋为空(认父不认子),尽管它并没有右儿子

    3.接着,我们更新一下(L)的信息(由下到上)

    4.最后,我们走向(L)的父亲(E)(通过虚边走向父亲)

    现在我们在(E)

    (E)转到根

    把抢了位置的(G)替换为上来的(L)(更新右儿子)

    更新(E)的信息(由下到上)

    走向(E)的父亲(A),注意我们已经换到了根,所以父亲为(A)(通过虚边走向父亲)

    (A)也进行这样的操作,那么直到再像父亲走的时候走到了空节点,结束

    总结,其实循环只有四步:

    1.转到根

    2.改变右儿子

    3.更新信息

    4.走向父亲

    void access(int x)//访问 
    {
    	for(int y = 0; x ;x = t[y = x].f)
        {
    		splay(x);
    		t[x].ch[1] = y;
    		up(x);
    	}
    }
    

    二、( ext{makeroot})(换根)

    懂了第一个操作,这个操作就显得简单很多

    连通(x)到根,把(x)转到根

    其它点的深度都改变了,但是我们发现他们的深度刚好都翻转了过来

    所以我们可以这样:

    void pushr(int x)//翻转操作
    {
    	swap(t[x].ch[0],t[x].ch[1]);
    	t[x].r ^= 1;
    }
    void makeroot(int x)//换根
    {
    	access(x);
    	splay(x);
    	pushr(x);
    } 
    

    三、( ext{findroot})(找根)

    就是找当前点所在的( ext{Splay})的根?

    Pi!

    是原树的根

    首先把(x)到根连通,把(x)转到根,一直向左走,最小的即为根

    int findroot(int x)//找根(原树中) ,就是最小的那个 
    {
    	access(x);
    	splay(x);
    	while(t[x].ch[0]) down(x),x = t[x].ch[0];
    	splay(x);
    	return x;
    }
    

    四、( ext{merge})(合并)

    自己给这个操作取的名字

    我们询问(x,y)的路径上的信息,就要把(x,y)合到一棵树里面

    故取名(merge),由于(merge)是关键字,所以简写为(merg) (好丑)

    我们将(x)置为树的根(( ext{makeroot})),通过( ext{access})操作连通(x,y),将(y)旋转到根,路径上的点就在(y)的子树中,我们可以通过访问(y)中的信息,获取路径上的信息

    void merg(int x,int y)//提取路径,把x和y搞到一棵Splay里面,再乱搞 
    {
    	makeroot(x);
    	access(y);
    	splay(y);
    }
    

    五、( ext{link})(加边)

    判断是否已经连通

    如果没连通,连虚边

    判连通的两个方法:

    1.普遍的

    (x)置为根,找(y)所在的树的根,如果不是(x),不连通,否则连通

    2.特殊的:只有加边

    并查集维护

    void link(int x,int y)//连边 
    {
    	makeroot(x);
    	if(findroot(y) != x) t[x].f = y;
    }
    

    六、( ext{cut})(删边)

    首先判断是否连通

    连通之后再判断是否有一条相连的边

    我们可以通过这样的方法判断:

    (x)置为根

    如果(y)的父亲是(x)并且(y)没有左子树(即(y)的祖先就(x)一个)

    删掉:(y)的父亲置为(0)(x)的右儿子置为(0)

    别忘了更新(x)的信息!!!

    void cut(int x,int y)//删边 
    {
    	makeroot(x);
    	if(findroot(y) == x && t[y].f == x && !t[y].ch[0])//保证它们相连 
    	{
    		t[y].f = t[x].ch[1] = 0;
    		up(x);
    	}
    }
    

    练习

    板题(洛谷)

    [COCI 2009] OTOCI / 极地旅行社(洛谷)

    弹飞绵羊(洛谷)

    洞穴勘测(洛谷)

    其实都是板题,就弹飞绵羊稍微有一点思维

    代码

    板题代码

    //12252024832524
    #include <cstdio>
    #include <algorithm>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 100005;
    int n,m;
    int v[MAXN];
    
    int Read()
    {
    	int x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    void Put1(int x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    void Put(int x)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x);
    }
    template <typename T>T Max(T x,T y){return x > y ? x : y;}
    template <typename T>T Min(T x,T y){return x < y ? x : y;}
    template <typename T>T Abs(T x){return x < 0 ? -x : x;}
    
    struct Splay//LinkCutTree
    {
    	int tot;
    	int st[MAXN];//栈 
    	struct node
    	{
    		int val,ch[2],f;
    		bool r;//翻转标记 
    	}t[MAXN];
    	
    	bool nroot(int x)//判断节点是否为一个Splay的根 
    	{
    		return t[t[x].f].ch[0] == x || t[t[x].f].ch[1] == x;
    		//如果连的是轻边,他的父亲的儿子里就没有它 
    	}
    	void pushr(int x)//翻转操作
    	{
    		swap(t[x].ch[0],t[x].ch[1]);
    		t[x].r ^= 1;
    	}
    	void up(int x)
    	{
    		t[x].val = t[t[x].ch[0]].val ^ t[t[x].ch[1]].val ^ v[x];
    	}
    	void down(int x)
    	{
    		if(t[x].r) 
    		{
    			if(t[x].ch[0]) pushr(t[x].ch[0]);
    			if(t[x].ch[1]) pushr(t[x].ch[1]);
    			t[x].r = 0;
    		}
    	}
    	bool chk(int x)
    	{
    		return (t[t[x].f].ch[1] == x);
    	}
    	void rotate(int x)
    	{
    		int y = t[x].f;
    		int z = t[y].f;
    		int k = chk(x);
    		int w = t[x].ch[k^1];
    		
    		t[y].ch[k] = w;
    		if(w) t[w].f = y;
    		
    		if(nroot(y)) t[z].ch[chk(y)] = x;
    		t[x].f = z;
    		
    		t[x].ch[k^1] = y;
    		t[y].f = x;
    		
    		up(y);
    		up(x);
    	}
    	void splay(int x,int to = 0)
    	{
    		int y = x,z = 0;
    		st[++z] = y;
    		while(nroot(y)) st[++z] = y = t[y].f;
    		while(z) down(st[z--]);
    		while(nroot(x))
    		{
    			y = t[x].f;
    			if(nroot(y))
    			{
    				if(chk(x) != chk(y)) rotate(x);
    				else rotate(y);
    			}
    			rotate(x);
    		}
    		up(x);
    	}
    	void access(int x)//访问 
    	{
    		for(int y = 0; x ;x = t[y = x].f)
            {
    			splay(x);
    			t[x].ch[1] = y;
    			up(x);
    		}
    	}
    	void makeroot(int x)//换根
    	{
    		access(x);
    		splay(x);
    		pushr(x);
    	} 
    	int findroot(int x)//找根(原树中) ,就是最小的那个 
    	{
    		access(x);
    		splay(x);
    		while(t[x].ch[0]) down(x),x = t[x].ch[0];
    		splay(x) ;
    		return x;
    	}
    	void merg(int x,int y)//提取路径,把x和y搞到一棵Splay里面,再乱搞 
    	{
    		makeroot(x);
    		access(y);
    		splay(y);
    	}
    	void link(int x,int y)//连边 
    	{
    		makeroot(x);
    		if(findroot(y) != x) t[x].f = y;
    	}
    	void cut(int x,int y)//删边 
    	{
    		makeroot(x);
    		if(findroot(y) == x && t[y].f == x && !t[y].ch[0])//保证它们相连 
    		{
    			t[y].f = t[x].ch[1] = 0;
    			up(x);
    		}
    	}
    }lct;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read();
    	m = Read();
    	for(int i = 1;i <= n;++ i) v[i] = Read();
    	while(m --)
    	{
    		int opt = Read(),x = Read(),y = Read();
    		if(opt == 0) {lct.merg(x,y);Put(lct.t[y].val);putchar('
    ');}
    		else if(opt == 1) {lct.link(x,y);}
    		else if(opt == 2) {lct.cut(x,y);}
    		else if(opt == 3) {lct.splay(x,0);v[x] = y;}
    		else printf("fuck ccf
    ");
    	}
    	return 0;
    }
    

    OTOCI代码 (貌似不开O2过不了,令人窒息的常数)

    //12252024832524
    #include <cstdio>
    #include <algorithm>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 30005;
    int n,m;
    int v[MAXN];
    int st[MAXN];//栈 
    int F[MAXN];
    char opt[10];
    
    inline int Read()
    {
    	int x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    inline void Put1(int x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    inline void Put(int x)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x);
    }
    template <typename T>T Max(T x,T y){return x > y ? x : y;}
    template <typename T>T Min(T x,T y){return x < y ? x : y;}
    template <typename T>T Abs(T x){return x < 0 ? -x : x;}
    
    char gc()
    {
    	char c = getchar();
    	while(c > 'z' || c < 'a') c = getchar();
    	return c;
    }
    int findSet(int x)
    {
    	if(F[x] != x) F[x] = findSet(F[x]);
    	return F[x];
    }
    struct Splay//LinkCutTree
    {
    	int tot;
    	struct node
    	{
    		int val,ch[2],f;
    		bool r;//翻转标记 
    	}t[MAXN];
    	
    	inline bool nroot(int x)//判断节点是否为一个Splay的根 
    	{
    		return t[t[x].f].ch[0] == x || t[t[x].f].ch[1] == x;
    		//如果连的是轻边,他的父亲的儿子里就没有它 
    	}
    	inline void pushr(int x)//翻转操作
    	{
    		swap(t[x].ch[0],t[x].ch[1]);
    		t[x].r ^= 1;
    	}
    	inline void up(int x)
    	{
    		t[x].val = t[t[x].ch[0]].val + t[t[x].ch[1]].val + v[x];
    	}
    	inline void down(int x)
    	{
    		if(t[x].r) 
    		{
    			if(t[x].ch[0]) pushr(t[x].ch[0]);
    			if(t[x].ch[1]) pushr(t[x].ch[1]);
    			t[x].r = 0;
    		}
    	}
    	inline bool chk(int x)
    	{
    		return (t[t[x].f].ch[1] == x);
    	}
    	inline void rotate(int x)
    	{
    		int y = t[x].f;
    		int z = t[y].f;
    		int k = chk(x);
    		int w = t[x].ch[k^1];
    		
    		t[y].ch[k] = w;
    		if(w) t[w].f = y;
    		
    		if(nroot(y)) t[z].ch[chk(y)] = x;
    		t[x].f = z;
    		
    		t[x].ch[k^1] = y;
    		t[y].f = x;
    		
    		up(y);
    		up(x);
    	}
    	inline void splay(int x,int to = 0)
    	{
    		int y = x,z = 0;
    		st[++z] = y;
    		while(nroot(y)) st[++z] = y = t[y].f;
    		while(z) down(st[z--]);
    		while(nroot(x))
    		{
    			y = t[x].f;
    			if(nroot(y))
    			{
    				if(chk(x) != chk(y)) rotate(x);
    				else rotate(y);
    			}
    			rotate(x);
    		}
    		up(x);
    	}
    	inline void access(int x)//访问 
    	{
    		for(int y = 0; x ;x = t[y = x].f)
            {
    			splay(x);
    			t[x].ch[1] = y;
    			up(x);
    		}
    	}
    	inline void makeroot(int x)//换根
    	{
    		access(x);
    		splay(x);
    		pushr(x);
    	} 
    	inline int findroot(int x)//找根(原树中) ,就是最小的那个 
    	{
    		access(x);
    		splay(x);
    		while(t[x].ch[0]) down(x),x = t[x].ch[0];
    		splay(x) ;
    		return x;
    	}
    	inline int dis(int x,int y)//询问路径,把x和y搞到一棵Splay里面,再乱搞 
    	{
    		makeroot(x);
    		access(y);
    		splay(y);
    		return t[y].val;
    	}
    	bool qconnect(int x,int y)
    	{
    		if(findSet(x) == findSet(y)) return 1;
    		return 0;
    	}
    	inline void link(int x,int y)//连边 
    	{
    		makeroot(x);
    		if(!qconnect(x,y)) t[x].f = y,printf("yes
    "),F[findSet(x)] = findSet(y);
    		else printf("no
    ");
    	}
    	inline void cut(int x,int y)//删边 
    	{
    		makeroot(x);
    		if(qconnect(x,y) && t[y].f == x && !t[y].ch[0])//保证它们相连 
    		{
    			t[y].f = t[x].ch[1] = 0;
    			up(x);
    		}
    	}
    }lct;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read();
    	for(register int i = 1;i <= n;++ i) v[i] = Read(),F[i] = i;
    	int x,y;
    	for(register int Q = Read(); Q ;-- Q)
    	{
    		char opt = gc();
    		x = Read();
    		y = Read();
    		if(opt == 'e') {if(!lct.qconnect(x,y)){printf("impossible
    ");continue;}Put(lct.dis(x,y));putchar('
    ');}
    		else if(opt == 'b') {lct.link(x,y);}
    		else if(opt == 'p') {lct.splay(x,0);v[x] = y;}
    	}
    	return 0;
    }
    

    弹飞绵羊代码

    //12252024832524
    #include <cstdio>
    #include <algorithm>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 200005;
    int n,m;
    int v[MAXN],tox[MAXN];
    int st[MAXN];//栈 
    
    inline int Read()
    {
    	int x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    inline void Put1(int x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    inline void Put(int x)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x);
    }
    template <typename T>T Max(T x,T y){return x > y ? x : y;}
    template <typename T>T Min(T x,T y){return x < y ? x : y;}
    template <typename T>T Abs(T x){return x < 0 ? -x : x;}
    
    char gc()
    {
    	char c = getchar();
    	while(c > 'z' || c < 'a') c = getchar();
    	return c;
    }
    struct Splay//LinkCutTree
    {
    	int tot;
    	struct node
    	{
    		int val,ch[2],f;
    		bool r;//翻转标记 
    	}t[MAXN];
    	
    	inline bool nroot(int x)//判断节点是否为一个Splay的根 
    	{
    		return t[t[x].f].ch[0] == x || t[t[x].f].ch[1] == x;
    		//如果连的是轻边,他的父亲的儿子里就没有它 
    	}
    	inline void pushr(int x)//翻转操作
    	{
    		swap(t[x].ch[0],t[x].ch[1]);
    		t[x].r ^= 1;
    	}
    	inline void up(int x)
    	{
    		t[x].val = t[t[x].ch[0]].val + t[t[x].ch[1]].val + v[x];
    	}
    	inline void down(int x)
    	{
    		if(t[x].r) 
    		{
    			if(t[x].ch[0]) pushr(t[x].ch[0]);
    			if(t[x].ch[1]) pushr(t[x].ch[1]);
    			t[x].r = 0;
    		}
    	}
    	inline bool chk(int x)
    	{
    		return (t[t[x].f].ch[1] == x);
    	}
    	inline void rotate(int x)
    	{
    		int y = t[x].f;
    		int z = t[y].f;
    		int k = chk(x);
    		int w = t[x].ch[k^1];
    		
    		t[y].ch[k] = w;
    		if(w) t[w].f = y;
    		
    		if(nroot(y)) t[z].ch[chk(y)] = x;
    		t[x].f = z;
    		
    		t[x].ch[k^1] = y;
    		t[y].f = x;
    		
    		up(y);
    		up(x);
    	}
    	inline void splay(int x,int to = 0)
    	{
    		int y = x,z = 0;
    		st[++z] = y;
    		while(nroot(y)) st[++z] = y = t[y].f;
    		while(z) down(st[z--]);
    		while(nroot(x))
    		{
    			y = t[x].f;
    			if(nroot(y))
    			{
    				if(chk(x) != chk(y)) rotate(x);
    				else rotate(y);
    			}
    			rotate(x);
    		}
    		up(x);
    	}
    	inline void access(int x)//访问 
    	{
    		for(int y = 0; x ;x = t[y = x].f)
            {
    			splay(x);
    			t[x].ch[1] = y;
    			up(x);
    		}
    	}
    	inline void makeroot(int x)//换根
    	{
    		access(x);
    		splay(x);
    		pushr(x);
    	} 
    	inline int findroot(int x)//找根(原树中) ,就是最小的那个 
    	{
    		access(x);
    		splay(x);
    		while(t[x].ch[0]) down(x),x = t[x].ch[0];
    		splay(x) ;
    		return x;
    	}
    	inline int dis(int x,int y)//询问路径,把x和y搞到一棵Splay里面,再乱搞 
    	{
    		makeroot(x);
    		access(y);
    		splay(y);
    		return t[y].val;
    	}
    	void link(int x,int y)//连边 
    	{
    		makeroot(x);
    		if(findroot(y) != x) t[x].f = y;
    	}
    	inline void cut(int x,int y)//删边 
    	{
    		makeroot(x);
    		if(findroot(y) == x && t[y].f == x && !t[y].ch[0])//保证它们相连 
    		{
    			t[y].f = t[x].ch[1] = 0;
    			up(x);
    		}
    	}
    }lct;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read();
    	for(register int i = 1;i <= n;++ i) 
    	{
    		v[i] = 1;
    		tox[i] = Read();
    		if(i + tox[i] > n)
    			tox[i] = n+1-i;
    		lct.link(i,i+tox[i]);
    	}
    	for(register int Q = Read(); Q ;-- Q)
    	{
    		int opt = Read(); 
    		if(opt == 1) Put(lct.dis(Read()+1,n+1)),putchar('
    ');
    		else if(opt == 2)
    		{
    			int x = Read()+1;
    			lct.cut(x,x+tox[x]);
    			tox[x] = Read();
    			if(x + tox[x] > n) tox[x] = n+1-x;
    			lct.link(x,x+tox[x]);
    		}
    		else printf("fuck ccf
    ");
    	}
    	return 0;
    }
    

    洞穴勘测

    //12252024832524
    #include <cstdio>
    #include <algorithm>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 10005;
    int n,m;
    int v[MAXN],tox[MAXN];
    int st[MAXN];//栈 
    
    inline int Read()
    {
    	int x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    inline void Put1(int x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    inline void Put(int x)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x);
    }
    template <typename T>T Max(T x,T y){return x > y ? x : y;}
    template <typename T>T Min(T x,T y){return x < y ? x : y;}
    template <typename T>T Abs(T x){return x < 0 ? -x : x;}
    
    char gc()
    {
    	char c = getchar();
    	while(c > 'Z' || c < 'A') c = getchar();
    	return c;
    }
    struct Splay//LinkCutTree
    {
    	int tot;
    	struct node
    	{
    		int val,ch[2],f;
    		bool r;//翻转标记 
    	}t[MAXN];
    	
    	inline bool nroot(int x)//判断节点是否为一个Splay的根 
    	{
    		return t[t[x].f].ch[0] == x || t[t[x].f].ch[1] == x;
    		//如果连的是轻边,他的父亲的儿子里就没有它 
    	}
    	inline void pushr(int x)//翻转操作
    	{
    		swap(t[x].ch[0],t[x].ch[1]);
    		t[x].r ^= 1;
    	}
    	inline void up(int x)
    	{
    		t[x].val = t[t[x].ch[0]].val + t[t[x].ch[1]].val + v[x];
    	}
    	inline void down(int x)
    	{
    		if(t[x].r) 
    		{
    			if(t[x].ch[0]) pushr(t[x].ch[0]);
    			if(t[x].ch[1]) pushr(t[x].ch[1]);
    			t[x].r = 0;
    		}
    	}
    	inline bool chk(int x)
    	{
    		return (t[t[x].f].ch[1] == x);
    	}
    	inline void rotate(int x)
    	{
    		int y = t[x].f;
    		int z = t[y].f;
    		int k = chk(x);
    		int w = t[x].ch[k^1];
    		
    		t[y].ch[k] = w;
    		if(w) t[w].f = y;
    		
    		if(nroot(y)) t[z].ch[chk(y)] = x;
    		t[x].f = z;
    		
    		t[x].ch[k^1] = y;
    		t[y].f = x;
    		
    		up(y);
    		up(x);
    	}
    	inline void splay(int x,int to = 0)
    	{
    		int y = x,z = 0;
    		st[++z] = y;
    		while(nroot(y)) st[++z] = y = t[y].f;
    		while(z) down(st[z--]);
    		while(nroot(x))
    		{
    			y = t[x].f;
    			if(nroot(y))
    			{
    				if(chk(x) != chk(y)) rotate(x);
    				else rotate(y);
    			}
    			rotate(x);
    		}
    		up(x);
    	}
    	inline void access(int x)//访问 
    	{
    		for(int y = 0; x ;x = t[y = x].f)
            {
    			splay(x);
    			t[x].ch[1] = y;
    			up(x);
    		}
    	}
    	inline void makeroot(int x)//换根
    	{
    		access(x);
    		splay(x);
    		pushr(x);
    	} 
    	inline int findroot(int x)//找根(原树中) ,就是最小的那个 
    	{
    		access(x);
    		splay(x);
    		while(t[x].ch[0]) down(x),x = t[x].ch[0];
    		splay(x) ;
    		return x;
    	}
    	inline int dis(int x,int y)//询问路径,把x和y搞到一棵Splay里面,再乱搞 
    	{
    		makeroot(x);
    		access(y);
    		splay(y);
    		return t[y].val;
    	}
    	bool qconnect(int x,int y)
    	{
    		makeroot(x);
    		if(findroot(y) == x) return 1;
    		return 0;
    	}
    	void link(int x,int y)//连边 
    	{
    		if(!qconnect(x,y)) t[x].f = y;
    	}
    	inline void cut(int x,int y)//删边 
    	{
    		if(qconnect(x,y) && t[y].f == x && !t[y].ch[0])//保证它们相连 
    		{
    			t[y].f = t[x].ch[1] = 0;
    			up(x);
    		}
    	}
    }lct;
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read();
    	for(register int Q = Read(); Q ;-- Q)
    	{
    		char c = gc();
    		if(c == 'C') lct.link(Read(),Read());
    		else if(c == 'D') lct.cut(Read(),Read());
    		else 
    		{
    			if(lct.qconnect(Read(),Read())) printf("Yes
    ");
    			else printf("No
    ");
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    【笔记】关于全栈开发、技术发展方向,软件开发模式的思考
    转 mysql 自动安装部署
    转shell不能执行su 后的脚本
    11.2 rman 备份 放在DG 库上跑,可能遇到的问题。
    highchart 学习
    每周定时备份linux 文件内容 到 远端主机
    转 javascript 本地时间 和utc 节点 和 时间戳转换 的
    转 钢铁侠的云之舞~多租户,Docker,虚拟机 , 你怎么选?
    转 perl 的调试
    转 pt-query-digest 使用实例
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/13395292.html
Copyright © 2020-2023  润新知