//LCT Link Cut Tree(动态树) 模板
//洛谷 P3690 【模板】Link Cut Tree (动态树)
//2020.11.10 创建
//2020.11.16 结束
//0 x y: 代表询问从 x 到 y 的路径上的点的权值的 xor 和。保证 x 到 y 是联通的。
//1 x y: 代表连接 x 到 y,若 x 到 y 已经联通则无需连接。
//2 x y: 代表删除边(x,y),不保证边(x,y) 存在。
//3 x y: 代表将点 x 上的权值变成 y。
#include <bits/stdc++.h>
#define ll long long
#define _for(i,a,b) for(int i = (a);i < (b); ++i)
#define sc scanf
#define pr printf
#define TLE ios::sync_with_stdio(false); cin.tie(0);
#define swap(x,y) x ^= y, y ^= x, x ^= y
const int maxn = 3e5+10;
const int INF = 0x3f3f3f3f;
using namespace std;
#define fa(x) tree[x].fath
#define ls(x) tree[x].ch[0]
#define rs(x) tree[x].ch[1]
struct Node {
int fath; //父节点
int ch[2]; //子节点
int s; //标记
bool r; //是否翻转
}tree[maxn];
int n,m;
int w[maxn];
//判断该节点是父亲的哪个儿子
int ident(int x) {
return ls(fa(x)) == x ? 0 : 1;
}
//连接
void connect(int x,int fa,int how) {
fa(x) = fa;
tree[fa].ch[how] = x;
return;
}
//判断节点是否为Splay的根节点
inline bool IsRoot(int x) {
return ls(fa(x)) != x && rs(fa(x)) != x;
//1.若x与fa(x)之间的边是虚边,那么它的父亲的孩子中不会有他(不在同一个splay内)
//2. splay的根节点与其父亲之间的边是虚边
}
//维护路径异或和
inline void push_up(int x) {
tree[x].s = tree[ls(x)].s ^ tree[rs(x)].s ^ w[x];
return;
}
//标记下传
void pushdown(int x) {
if(tree[x].r) {
swap(ls(x),rs(x));
tree[ls(x)].r ^= 1;
tree[rs(x)].r ^= 1;
tree[x].r = 0;
}
}
/*
void rotate(int x) {
int Y = fa(x), R = fa(Y), Yson = ident(x), Rson = ident(Y);
int B = tree[x].ch[Yson ^ 1];
fa(x) = R;
if(!IsRoot(Y))
connect(x, R, Rson);
//这里如果不判断y是否根节点,那么当y是根节点的时候,0节点的儿子就会被更新为x
//这样x就永远不能被判断为根节点,也就会无限循环下去了
//但是这里不更新x的父亲的话就会出现无限递归的情况
connect(B, Y, Yson);
connect(Y, x, Yson ^ 1);
push_up(Y); push_up(x);
}
*/
#define id(x,y) tree[x].ch[1] == y
void rotate(int x) { //单旋
int y = fa(x);
int z = fa(y);
int k = id(y,x); // k = 1 则x是y的右儿子
// k = 0 则x是y的左儿子
//这里如果不判断y是否根节点,那么当y是根节点的时候,0节点的儿子就会被更新为x
//这样x就永远不能被判断为根节点,也就会无限循环下去了
//但是这里不更新x的父亲的话就会出现无限递归的情况
if(!IsRoot(y))
tree[z].ch[id(z,y)] = x; // y < z -> x < z;
fa(x) = z; // y > z -> x < z;
tree[y].ch[k] = tree[x].ch[k ^ 1]; // 如果当前节点是x的k孩子,旋转之后则必是y的!k孩子
fa(tree[x].ch[k ^ 1]) = y;
tree[x].ch[k ^ 1] = y; // 若x是y的左儿子,则y必是x的右儿子,反之同理
fa(y) = x;
push_up(y);
push_up(x);
}
int st[maxn];
void Splay(int x) {
int now = x, top = 0;
st[++top] = now;
while(!IsRoot(now)) st[++top] = now = fa(now);
while(top) pushdown(st[top--]);
/*
for(int now = fa(x); !IsRoot(x); rotate(x), now = fa(x))//只要不是根就转
if(!IsRoot(now))
rotate( ident(x) == ident(now) ? x : now );
*/
while(!IsRoot(x)) {
int y = fa(x);
if(!IsRoot(y))
rotate( (ls(y) == x) ^ (ls(fa(y)) == y) ? x : y );
rotate(x);
}
}
//将根节点到x点的路径变为实路径,且x与其儿子之间的边都变为虚边
inline void access(int x) {
for(int y = 0; x; x = fa(y = x)) {
Splay(x);
rs(x) = y;
push_up(x);
}
}
//将x变为原树的根
inline void makeroot(int x) {
access(x);
Splay(x);
tree[x].r ^= 1;
pushdown(x);
}
//找到x所在原树的根
inline int findroot(int x) {
access(x);
Splay(x);
pushdown(x);
while(ls(x)) pushdown(x = ls(x));
return x;
}
//获取x->y所对应的路径
inline void split(int x,int y) {
makeroot(x); //将x置于原树根节点位置
access(y); //将x->y连成Splay
Splay(y); //将y变为当前Splay的根节点
}
//连接x,y
inline void link(int x,int y) {
makeroot(x);
if(findroot(y) != x) tree[x].fath = y;
}
//断开边x-y
inline void cut(int x,int y) {
makeroot(x);
if(findroot(y) == x //x,y在同一棵原树上
&& fa(x) == y //x的父亲为y
&& ls(y) == x //y的左儿子为x
&& !rs(x)) //没有比x深度更大的节点
{
fa(x) = ls(y) = 0;
push_up(y);
}
}
int main() {
sc("%d %d",&n,&m);
_for(i,1,n+1) {
sc("%d",&w[i]);
}
int opt,x,y;
_for(i,1,m+1) {
sc("%d %d %d",&opt,&x,&y);
//0 x y: 代表询问从 x 到 y 的路径上的点的权值的 xor 和。保证 x 到 y 是联通的。
if(opt == 0) {
split(x,y);
pr("%d\n",tree[y].s);
}
//1 x y: 代表连接 x 到 y,若 x 到 y 已经联通则无需连接。
else if(opt == 1) {
link(x,y);
}
//2 x y: 代表删除边(x,y),不保证边(x,y) 存在。
else if(opt == 2) {
cut(x,y);
}
//3 x y: 代表将点 x 上的权值变成 y。
else if(opt == 3) {
Splay(x);
w[x] = y;
}
}
}
07-16
07-16
07-16
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交