动态树与 LCT

用途

LCT(Link Cut Tree) 是一种用于处理一个有根的森林中各棵树之间的动态操作。LCT 能维护动态树的的平衡性,以 \(O(\log n)\) 的均摊复杂度进行合并、删除、查询等操作。

LCT 的思想

LCT 结合了树链剖分和 Splay 树的思路。其核心思想为 “原树实链从上到下,对应 Splay 树从左到右”

  1. 实链剖分
  2. 转换原树为辅助树,用辅助树维护原树,在代码中只需维护辅助树。
  3. LCT 借用了 Splay 树的操作方法处理实链。

从原树到辅助树

  1. 将原树剖分为实链和虚边
  2. 将原树根据实链和虚边转换为辅助树,
  3. 用 Splay 树处理实链(节点在 Splay 中的优先级为原树上节点的深度)

转化为

LCT 的性质

  1. 辅助树表达原树的路径形态。
  2. 原树的一条实链对应辅助树上的一颗 Splay 树。
  3. 原树实链从上到下,对应 Splay 树从左到右。
  4. 辅助树中的各棵 Splay 树用虚边链接。

LCT 的操作

  1. 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);
}
  1. 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);
    }
}
  1. makert(\(x\)) :把 \(x\) 在原树上旋转到根的位置。
void makert(int x)
{
    access(x);
    splay(x);
    reverse(x);
}
  1. findrt(\(x\)) :查找 \(x\) 在原树上的根,常用于判断两节点是否连通。
int findrt(int x)
{
    access(x);
    splay(x);
    while(ls) pd(x),x = ls;
    return x;
}
  1. split(\(x,y\)) :在原树上生成一条 \(x\)\(y\) 的实链。
void split(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
}
  1. link(\(x,y\)) :连接 \(x\)\(y\)
void link(int x,int y)
{
    makeroot(x);
    t[x].fa = y;
}
  1. 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);
}
  1. 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 的应用

  1. 判断连通性。
  2. 求两点距离。
  3. 求 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;
}

习题

Luogu P3960

Luogu P2147

Luogu P4312

Luogu P1501

posted @ 2025-04-18 19:21  IC0CI  阅读(17)  评论(0)    收藏  举报