动态树与 LCT
用途
LCT(Link Cut Tree) 是一种用于处理一个有根的森林中各棵树之间的动态操作。LCT 能维护动态树的的平衡性,以 \(O(\log n)\) 的均摊复杂度进行合并、删除、查询等操作。
LCT 的思想
LCT 结合了树链剖分和 Splay 树的思路。其核心思想为 “原树实链从上到下,对应 Splay 树从左到右”。
- 实链剖分
- 转换原树为辅助树,用辅助树维护原树,在代码中只需维护辅助树。
- LCT 借用了 Splay 树的操作方法处理实链。
从原树到辅助树
- 将原树剖分为实链和虚边
- 将原树根据实链和虚边转换为辅助树,
- 用 Splay 树处理实链(节点在 Splay 中的优先级为原树上节点的深度)
转化为
LCT 的性质
- 辅助树表达原树的路径形态。
- 原树的一条实链对应辅助树上的一颗 Splay 树。
- 原树实链从上到下,对应 Splay 树从左到右。
- 辅助树中的各棵 Splay 树用虚边链接。
LCT 的操作
- splay(\(x\)) :将辅助树的节点 \(x\) 转到其所在 Splay 树的根,操作并不影响原树。
void splay(int x)
{
    int y,z;
    pd(x);
    while(!isrt(x))
    {
        y = t[x].fa,z = t[y].fa;
        if(!isrt(y))
        {
            if((t[z].son[0] == y) ^ (t[y].son[0] == x)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    pu(x);
}
- access(\(x\)) :在原树上创建一条从根到节点 \(x\) 的实链,即辅助树上一颗 Splay。
void access(int x)
{
    for(int s = 0;x;s = x,x = t[x].fa)
    {
        splay(x);
        rs = s;
        pu(x);
    }
}
- makert(\(x\)) :把 \(x\) 在原树上旋转到根的位置。
void makert(int x)
{
    access(x);
    splay(x);
    reverse(x);
}
- findrt(\(x\)) :查找 \(x\) 在原树上的根,常用于判断两节点是否连通。
int findrt(int x)
{
    access(x);
    splay(x);
    while(ls) pd(x),x = ls;
    return x;
}
- split(\(x,y\)) :在原树上生成一条 \(x\) 到 \(y\) 的实链。
void split(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
}
- link(\(x,y\)) :连接 \(x\) 和 \(y\)。
void link(int x,int y)
{
    makeroot(x);
    t[x].fa = y;
}
- cut(\(x,y\)) :断开 \(x\) 和 \(y\)。
void cut(int x,int y)
{
    split(x,y);
    if(t[y].son[0] != x || rs) return;
    t[x].fa = t[y].son[0] = 0;
    pu(x);
}
- isrt(\(x\)) :判断 \(x\) 是否为它所在的 Splay 树的根。
bool isrt(int x)
{
    int g = t[x].fa;
    return t[g].son[0] != x && t[g].son[1] != x;
}
LCT 的应用
- 判断连通性。
- 求两点距离。
- 求 LCA。
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
//快读
const int N = 3e5 + 5;
struct node
{
    int fa,son[2],sum,val,tag;
}t[N];
#define ls t[x].son[0]
#define rs t[x].son[1]
bool isrt(int x)
{
    int g = t[x].fa;
    return t[g].son[0] != x && t[g].son[1] != x;
}
void pu(int x)
{
    t[x].sum = t[x].val ^ t[ls].sum ^ t[rs].sum;
}
void reverse(int x)
{
    if(!x) return;
    swap(ls,rs);
    t[x].tag ^= 1;
}
void pushdown(int x)
{
    if(t[x].tag)
    {
        reverse(ls);
        reverse(rs);
        t[x].tag = 0;
    }
}
void pd(int x)
{
    if(!isrt(x)) pd(t[x].fa);
    pushdown(x);
}
void rotate(int x)
{
    int y = t[x].fa;
    int z = t[y].fa;
    int k = (t[y].son[1] == x);
    if(!isrt(y)) t[z].son[t[z].son[1] == y] = x;
    t[x].fa = z;
    t[y].son[k] = t[x].son[k ^ 1];
    if(t[x].son[k ^ 1]) t[t[x].son[k ^ 1]].fa = y;
    t[y].fa = x;
    t[x].son[k ^ 1] = y;
    pu(y);
}
void splay(int x)
{
    int y,z;
    pd(x);
    while(!isrt(x))
    {
        y = t[x].fa,z = t[y].fa;
        if(!isrt(y))
        {
            if((t[z].son[0] == y) ^ (t[y].son[0] == x)) rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    pu(x);
}
void access(int x)
{
    for(int s = 0;x;s = x,x = t[x].fa)
    {
        splay(x);
        rs = s;
        pu(x);
    }
}
void makert(int x)
{
    access(x);
    splay(x);
    reverse(x);
}
void split(int x,int y)
{
    makert(x);
    access(y);
    splay(y);
}
void link(int x,int y)
{
    makert(x);
    t[x].fa = y;
}
void cut(int x,int y)
{
    split(x,y);
    if(t[y].son[0] != x || rs) return;
    t[x].fa = t[y].son[0] = 0;
    pu(x);
}
int findrt(int x)
{
    access(x);
    splay(x);
    while(ls) pd(x),x = ls;
    return x;
}
signed main()
{
    int n = rd(),m = rd();
    for(int i = 1;i <= n;i++)
    {
        t[i].val = rd();
        t[i].sum = t[i].val;
    }
    while(m--)
    {
        int opt = rd(),a = rd(),b = rd();
        if(opt == 0)
        {
            split(a,b);
            cout << t[b].sum << '\n';
        }
        if(opt == 1) if(findrt(a) != findrt(b)) link(a,b);
        if(opt == 2) cut(a,b);
        if(opt == 3) splay(a),t[a].val = b;
    }
    return 0;
}
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号