>Link
luogu P3690
>Description
给定 n 个点以及每个点的权值,要你处理接下来的 m 个操作。
操作有四种,操作从 0 到 3 编号。点从 1 到 n 编号。
- x y 代表询问从 x 到 y 的路径上的点的权值的 xor 和。保证 x 到 y 是联通的。
- x y 代表连接 x 到 y,若 x 到 y 已经联通则无需连接。
- x y 代表删除边 (x,y),不保证边 (x,y) 存在。
- x y 代表将点 x 上的权值变成 y。
n ≤ 1 0 5 , m ≤ 3 ∗ 1 0 5 n\le10^5,m\le3*10^5 n≤105,m≤3∗105
>解题思路
(本来好久之前就要写了,但是一直在改其他题,之前打的也搞不见了,所以一直咕到现在QwQ)
随便说几句:
在树上建很多个splay,每个splay包含的点在树上为一条链,并且深度互不相同(splay按深度建)
这样我们可以根据splay和树的性质搞很多操作
节点的关系:“认父不认子”,在同一棵树上但不在同一个splay上;“认父认子”,在同一棵树上也在同一个splay上
a
c
c
e
s
s
access
access:打通
x
x
x到树上根节点的路,并且这个splay只包含根节点到
x
x
x路径上的点
m
a
k
e
r
o
o
t
makeroot
makeroot:把
x
x
x变成树上的根节点,先
a
c
c
e
s
s
access
access它,它变成根节点后显然在splay中它从深度最大变成深度最小的了…深度最小的变成深度最大的了,所以我们把整个splay翻转一下,这里用到翻转的懒标记
s
p
l
i
t
split
split:打通
x
x
x和
y
y
y之间的路径,那就把
x
x
x变成根,然后打通
y
y
y到根,再splay一下
y
y
y(把它变成splay中的根)
c
o
n
n
e
c
t
connect
connect:在
x
x
x和
y
y
y之间连条边,直接让其中一个点变成树根,然后让它的父亲变成另一个点,不然它原来的父亲就会被刷掉
c
u
t
cut
cut:切断
x
x
x和
y
y
y之间的边,先
s
p
l
i
t
split
split它们,然后根据
x
x
x是树根、
y
y
y是splay根的性质,把他们之间的关系删掉(“不认子也不认父”)
…然后其他很多操作都可以根据这些性质搞
p u s h a l l pushall pushall:注意因为是边用了翻转的懒标记边splay,所以在splay第x个节点前,我们把 x x x到树根上路径的点的懒标记全部操作完先
>代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 300010
using namespace std;
struct tree
{
int val, sum, lazy, f, son[3];
} t[N];
int n, m;
void update (int x)
{
int ls = t[x].son[0], rs = t[x].son[1];
t[x].sum = t[x].val;
if (ls) t[x].sum ^= t[ls].sum;
if (rs) t[x].sum ^= t[rs].sum;
}
int identify (int x)
{
if (t[t[x].f].son[0] == x) return 0;
if (t[t[x].f].son[1] == x) return 1;
return -1;
}
void pushdown (int x)
{
if (!t[x].lazy) return;
swap (t[x].son[0], t[x].son[1]);
if (t[x].son[0]) t[t[x].son[0]].lazy ^= 1;
if (t[x].son[1]) t[t[x].son[1]].lazy ^= 1;
t[x].lazy = 0;
}
void pushall (int x)
{
if (identify (x) != -1) pushall (t[x].f);
pushdown (x);
}
void rorate (int x)
{
int y = t[x].f, z = t[y].f;
int k = identify (x), kk = identify (y);
t[y].son[k] = t[x].son[k ^ 1], t[t[x].son[k ^ 1]].f = y;
t[x].son[k ^ 1] = y, t[y].f = x;
if (kk != -1) t[z].son[kk] = x;
t[x].f = z;
update (y);
update (x);
}
void splay (int x)
{
pushall (x);
int y, z, k, kk;
while (identify (x) != -1)
{
y = t[x].f;
z = t[y].f;
k = identify (x), kk = identify (y);
if (kk != -1)
{
if (k == kk) rorate (y);
else rorate (x);
}
rorate (x);
}
}
void access (int x)
{
int u = 0;
while (x)
{
splay (x);
t[x].son[1] = u;
update (x);
u = x;
x = t[x].f;
}
}
void makeroot (int x)
{
access (x);
splay (x);
t[x].lazy ^= 1;
}
void split (int x, int y)
{
makeroot (x);
access (y);
splay (y);
}
int asksum (int x, int y)
{
split (x, y);
return t[y].sum;
}
int findroot (int x)
{
access (x);
splay (x);
while (t[x].son[0]) pushdown (x), x = t[x].son[0];
splay (x);
return x;
}
void connect (int x, int y)
{
makeroot (x);
if (x == findroot (y)) return;
t[x].f = y;
}
void cut (int x, int y)
{
if (findroot (x) != findroot (y)) return;
split (x, y);
if (t[x].f != y || t[x].son[1]) return;
t[x].f = t[y].son[0] = 0;
update (y);
}
void change (int x, int val)
{
makeroot (x);
t[x].val = val;
update (x);
}
int main()
{
scanf ("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf ("%d", &t[i].val);
t[i] = (tree){t[i].val, t[i].val, 0, 0, {0, 0, 0}};
}
int type, x, y;
while (m--)
{
scanf ("%d%d%d", &type, &x, &y);
if (type == 0) printf ("%d\n", asksum (x, y));
else if (type == 1) connect (x, y);
else if (type == 2) cut (x, y);
else change (x, y);
}
return 0;
}