zoukankan      html  css  js  c++  java
  • 【洛谷P3690】【模板】Link Cut Tree (动态树)

    题目

    题目链接:https://www.luogu.com.cn/problem/P3690
    给定 (n) 个点以及每个点的权值,要你处理接下来的 (m) 个操作。
    操作有四种,操作从 (0)(3) 编号。点从 (1)(n) 编号。

    • 0 x y 代表询问从 (x)(y) 的路径上的点的权值的 ( ext{xor}) 和。保证 (x)(y) 是联通的。
    • 1 x y 代表连接 (x)(y),若 (x)(y) 已经联通则无需连接。
    • 2 x y 代表删除边 ((x,y)),不保证边 ((x,y)) 存在。
    • 3 x y 代表将点 (x) 上的权值变成 (y)

    思路

    LCT 维护了若干棵 Splay,每一棵 Splay 维护的是树上深度连续递增的一条链,且中序遍历为这条链深度从浅到深的节点。
    与重剖和长剖不同,LCT 树剖采用的是实链剖分,也就是每一个点与其儿子中,最多有一条是实边,这条实边所连接的儿子与该点位于同一棵 Splay 中。其他儿子都不与该点位于同一棵 Splay。这样就保证了每一棵 Splay 对应原树中的一条链。
    虽然虚边不需要在同一个 Splay 中,但是虚边所对应的儿子的 (fa) 数组依然指向其父亲,也就是认父不认子。
    LCT 最关键的两个操作是 access 和 split,分别是打通一个点到树根的链,扔进一棵 Splay 里;和打通两个点所在链。
    对于 access 操作,我们直接不断将 (x) splay 到其所在 Splay 的根,然后判断如果依然有父节点,说明上面是一条虚边而不是到达了树根,那么就打通这条虚边,将原来的实边变为虚边,然后继续操作直到树根。
    对于 split 操作,我们先把 (x) access 到树根,然后将 (y) splay 到其 Splay 的根,如果 (y)(x) 并不在同一棵 Splay 中,那么就令 (fa_y=x)
    Link-Cut Tree 自然支持 Link 和 Cut,也就是连边和删边,利用各种辅助操作即可完成。
    当然 LCT 还有很多辅助操作如 findrt,makert,notrt 等,以及 Splay 的基本操作 rotate,splay。在此不一一细说。可以看 Blog
    时间复杂度 (O(mlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=100010;
    int n,m;
    
    struct LCT
    {
    	int val[N],ch[N][2],fa[N],xors[N];
    	bool rev[N];
    	
    	void pushup(int x)
    	{
    		xors[x]=xors[ch[x][0]]^xors[ch[x][1]]^val[x];
    	}
    	
    	void pushdown(int x)
    	{
    		if (rev[x])
    		{
    			int c=ch[x][0]; swap(ch[c][0],ch[c][1]);
    			c=ch[x][1]; swap(ch[c][0],ch[c][1]);
    			rev[ch[x][0]]^=1; rev[ch[x][1]]^=1; rev[x]=0;
    		}
    	}
    	
    	bool notrt(int x)
    	{
    		return (ch[fa[x]][0]==x) || (ch[fa[x]][1]==x);
    	}
    	
    	int pos(int x)
    	{
    		return ch[fa[x]][1]==x;
    	}
    	
    	void rotate(int x)
    	{
    		int y=fa[x],z=fa[y],k=pos(x),c=ch[x][k^1];
    		if (notrt(y)) ch[z][pos(y)]=x; ch[x][k^1]=y; ch[y][k]=c;
    		if (c) fa[c]=y; fa[y]=x; fa[x]=z;
    		pushup(y); pushup(x);
    	}
    	
    	void splay(int x)
    	{
    		stack<int> st;
    		st.push(x);
    		for (int y=x;notrt(y);y=fa[y]) st.push(fa[y]);
    		for (;st.size();st.pop()) pushdown(st.top());
    		while (notrt(x))
    		{
    			if (notrt(fa[x]))
    				rotate(pos(x)==pos(fa[x])?fa[x]:x);
    			rotate(x);
    		}
    		pushup(x);
    	}
    	
    	void access(int x)
    	{
    		for (int y=0;x;y=x,x=fa[x])
    		{
    			splay(x); ch[x][1]=y;
    			pushup(x);
    		}
    	}
    	
    	int findrt(int x)
    	{
    		access(x); splay(x);
    		for (;ch[x][0];x=ch[x][0])
    			pushdown(x);
    		splay(x);
    		return x;
    	}
    	
    	void makert(int x)
    	{
    		access(x); splay(x);
    		rev[x]^=1; swap(ch[x][0],ch[x][1]);
    	}
    	
    	void split(int x,int y)
    	{
    		makert(x);
    		access(y);
    		splay(y);
    	}
    	
    	void link(int x,int y)
    	{
    		makert(x);
    		if (findrt(y)!=x) fa[x]=y;
    		access(x);
    	}
    	
    	void cut(int x,int y)
    	{
    		makert(x);
    		if (findrt(y)!=x) return;
    		splay(x);
    		if (fa[y]==x && !ch[y][0])
    			fa[y]=ch[x][1]=0;
    		pushup(x);
    	}
    	
    	int query(int x,int y)
    	{
    		split(x,y);
    		return xors[y];
    	}
    }lct;
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    		scanf("%d",&lct.val[i]);
    	while (m--)
    	{
    		int opt,x,y;
    		scanf("%d%d%d",&opt,&x,&y);
    		if (opt==0) printf("%d
    ",lct.query(x,y));
    		if (opt==1) lct.link(x,y);
    		if (opt==2) lct.cut(x,y);
    		if (opt==3) lct.splay(x),lct.val[x]=y;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Android Media Playback 中的MediaPlayer的用法及注意事项(二)
    Android Media Playback 中的MediaPlayer的用法及注意事项(一)
    34. Search for a Range
    33. Search in Rotated Sorted Array
    32. Longest Valid Parentheses
    31. Next Permutation下一个排列
    30. Substring with Concatenation of All Words找出串联所有词的子串
    29. Divide Two Integers
    28. Implement strStr()子串匹配
    27. Remove Element
  • 原文地址:https://www.cnblogs.com/stoorz/p/14242855.html
Copyright © 2011-2022 走看看