【学习笔记】Link Cut Tree(动态树)学习笔记
将一棵树虚实链剖分,剖分成若干条链,每一条链存在一个splay里面,这个splay里面的边由实边构成,splay与splay之间如果有边,连的是虚边
一个splay的中序遍历即这一条链从深度小到深度大的顺序
核心操作:access(x):把x到根节点这条链提出来放在一个splay中
常见问题模型:维护链上的信息、动态维护连通性(只连不断)、维护边权(化边为点)、维护子树信息、维护树上染色连通块(每一个splay代表一个染色连通块)等
#include<bits/stdc++.h>
using namespace std;
#define Re register int
const int N = 100005;
int n, m, res, g[N], val[N], sum[N], fa[N], son[N][2];
bool lz[N];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline void write(int x)
{
if (!x)
{
puts("0");
return;
}
int num = 0;
char sc[15];
while (x) sc[++num] = x % 10 + 48, x /= 10;
while (num) putchar(sc[num--]);
putchar('\n');
}
inline void push_up(int x)//上传标记
{
sum[x] = sum[son[x][0]] ^ sum[son[x][1]] ^ val[x];
}
inline void rese(int x)//翻转
{
lz[x] ^= 1, son[x][0] ^= son[x][1] ^= son[x][0] ^= son[x][1];
}
inline void push_down(int x)//下传懒标记
{
lz[x] = 0;
if (son[x][0]) rese(son[x][0]);
if (son[x][1]) rese(son[x][1]);
}
inline bool check(int x)//判断x和它父亲是否在同一个splay内
{
return son[fa[x]][0] == x || son[fa[x]][1] == x;
}
inline void rotate(int x)//旋转x到它的父亲
{
int y = fa[x], z = fa[y];
bool p = son[y][1] ^ x;
if (check(y)) son[z][son[z][1] == y] = x;//son[z][1]==y 不能写成son[z][0]^y 因为son[z][1]==y时(son[z][1]==y)=1,而(son[z][0]^y)>0,可能>1
fa[x] = z, fa[y] = x;
if (son[x][p]) fa[son[x][p]] = y;
son[y][p ^ 1] = son[x][p], son[x][p] = y;
push_up(y), push_up(x);
}
inline void splay(int x)//把x旋转到当前splay的根
{
int y = x;
g[res = 1] = x;
while (check(y)) y = fa[y], g[++res] = y;
while (res)
{
if (lz[g[res]]) push_down(g[res]);
--res;
}
while (check(x))
{
y = fa[x];
if (check(y)) rotate(((son[y][0] == x) ^ (son[fa[y]][0] == y)) ? x : y);
rotate(x);
}
}
inline void access(int x)//把根节点到x这条链提出来到一个splay,中序遍历为根节点到x
{
for (Re i = 0; x; x = fa[i = x])
splay(x), son[x][1] = i, push_up(x);
}
inline void make_root(int x)//让x成为原树上的根
{
access(x), splay(x), rese(x);
}
inline int find_root(int x)//返回x所在原树上的根
{
access(x), splay(x);
while (1)
{
if (lz[x]) push_down(x);
if (!son[x][0]) return x;
x = son[x][0];
}
}
inline int get_ans(int x, int y)//x到y这条链上权值的异或和
{
make_root(x), access(y), splay(y);
return sum[y];
}
inline void link(int x, int y)//如果x和y不联通的话,连接x和y
{
make_root(x);
if (find_root(y) ^ x) fa[x] = y, push_up(y);
}
inline void cut(int x, int y)//如果原树上存在边(x,y),删除掉这条边
{
make_root(x), access(y), splay(y);
if (fa[x] == y && !son[x][1]) fa[x] = son[y][0] = 0, sum[y] = val[y];
}
int main()
{
n = read(), m = read();
for (Re i = 1; i <= n; ++i) val[i] = sum[i] = read();
for (Re i = 0; i < m; ++i)
{
int opt = read(), x = read(), y = read();
if (!opt) write(get_ans(x, y));
else if (opt == 1) link(x, y);
else if (opt == 2) cut(x, y);
else splay(x), val[x] = y, push_up(x);
}
return 0;
}
例题:
【Sol】1. [SDOI2017]树点涂色:洛谷P3703、LOJ#2001
【Sol】2. [SHOI2014]三叉神经树:洛谷P4332、LOJ#2187
参考资料:

浙公网安备 33010602011771号