模板链接:https://www.luogu.com.cn/problem/P3690
给定 n 个点以及每个点的权值,要你处理接下来的 m 个操作。
操作有四种,操作从 0 到 3 编号。点从 1 到 n 编号。
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 rep(i, a, b) for(int i=(int)a;i<=(int)b;i++)
#define pb push_back
#define fi first
#define se second
#define debug(x) cerr<<#x<<"="<<x<<endl;
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 300005;
const int maxm = 60005;
const int inf = 0x3f3f3f3f;
int a[maxn], n, m;
struct Link_Cut_Tree {
int Top, Son[maxn][2], fa[maxn], W[maxn], Q[maxn], laz[maxn];
int is_rightson(int x) {
return x == Son[fa[x]][1];
}
//儿子更新后要对父亲进行更新
void push_up(int rt) {
W[rt] = W[Son[rt][1]] ^ W[Son[rt][0]] ^ a[rt];
}
//延迟标记向下更新
void push_down(int rt) {
int &L = Son[rt][0], &R = Son[rt][1];
if (laz[rt]) {
laz[rt] ^= 1, laz[L] ^= 1, laz[R] ^= 1;
swap(L, R);
}
}
//因为Splay的树根的父亲并不记录这个节点(连向该节点的为轻链)
bool is_root(int rt) {
return Son[fa[rt]][0] != rt && Son[fa[rt]][1] != rt;
}
//将x变成其父亲的父节点
void Rotate(int rt) {
int f = fa[rt], ff = fa[f], L, R;
R = (L = is_rightson(rt)) ^ 1;
if (!is_root(f)) {
//如果f不是根节点,那么ff的儿子f会变成rt
Son[ff][Son[ff][1] == f] = rt;
}
fa[rt] = ff, fa[f] = rt, fa[Son[rt][R]] = f;
Son[f][L] = Son[rt][R], Son[rt][R] = f;
push_up(f), push_up(rt);
}
void Splay(int rt) {
Q[Top = 1] = rt;
for (int i = rt; !is_root(i); i = fa[i]) {
Q[++Top] = fa[i];
}
for (int i = Top; i > 0; i--) {
push_down(Q[i]);
}
//将x旋转为根
while (!is_root(rt)) {
int f = fa[rt], ff = fa[f];
if (!is_root(f)) (Son[f][0] == rt) ^ (Son[ff][0] == f) ? Rotate(rt) : Rotate(f);
Rotate(rt);
}
}
void Access(int rt) {
//向根连接一条重链
for (int i = 0; rt; i = rt, rt = fa[rt]) {
Splay(rt), Son[rt][1] = i, push_up(rt);
}
}
//把rt变成根
void make_root(int rt) {
Access(rt), Splay(rt);
laz[rt] ^= 1;
}
//找rt根节点
int find_root(int rt) {
Access(rt), Splay(rt);
while (Son[rt][0]) {
rt = Son[rt][0];
}
return rt;
}
//拉出一条x到y的路径为一个Splay
void Split(int x, int y) {
make_root(x), Access(y), Splay(y);
}
//删除一条x到y的边
void cut_edge(int x, int y) {
Split(x, y);
if (Son[x][1] || fa[x] != y || Son[y][is_rightson(x) ^ 1]) return;
Son[y][0] = fa[x] = 0;
}
//连一条x到y的轻边
void link_light_edge(int x, int y) {
make_root(x);
fa[x] = y;
}
} LCT;
int read() {
int ans = 0;
char last = ' ', ch = getchar();
while (ch < '0' || ch > '9')last = ch, ch = getchar();
while (ch >= '0' && ch <= '9')ans = ans * 10 + ch - '0', ch = getchar();
if (last == '-')ans = -ans;
return ans;
}
int main() {
#ifdef LOCAL_JUDGE
freopen("in.txt", "r+", stdin);
#endif
n = read(), m = read();
rep(i, 1, n) {
a[i] = read();
LCT.W[i] = a[i];
}
while (m--) {
int op = read(), x = read(), y = read();
if (op == 0) {
LCT.Split(x, y);
printf("%d\n", LCT.W[y]);
} else if (op == 1) {
if (LCT.find_root(x) != LCT.find_root(y)) {
LCT.link_light_edge(x, y);
}
} else if (op == 2) {
if (LCT.find_root(x) == LCT.find_root(y)) {
LCT.cut_edge(x, y);
}
} else if (op == 3) {
a[x] = y;
LCT.Access(x), LCT.Splay(x), LCT.push_up(x);
}
}
return 0;
}