首页 > 其他分享 >动态树(Link Cut Tree)

动态树(Link Cut Tree)

时间:2023-03-06 21:13:58浏览次数:55  
标签:ch int void Tree Splay Cut Link il ri

动态树(Link Cut Tree)

简介

Link/Cut Tree 是一种数据结构,我们用它来解决动态树问题。
Link/Cut Tree 又称 \(Link-Cut Tree\),简称 \(LCT\),但它不叫动态树,动态树是指一类问题。
Splay Tree 是 \(LCT\) 的基础,但是 \(LCT\) 用的 \(Splay Tree\) 和普通的 \(Splay\) 在细节处不太一样(进行了一些扩展)。
这是一个和 \(Splay\) 一样只需要写几 \((yi)\) 个 \((dui)\) 核心函数就能实现一切的数据结构。

基本操作

struct tree{
    int f,ch[2],val,sum,lz;
}t[N];

一般数据结构函数

il void PushUp(ri int x){
    t[x].sum=t[t[x].ch[0]].sum^t[t[x].ch[1]].sum^t[x].val;
}

il void PushDown(ri int x){
    if(t[x].lz){
        if(t[x].ch[0]) Reverse(t[x].ch[0]);
        if(t[x].ch[1]) Reverse(t[x].ch[1]);
        t[x].lz=0;
    }
}

\(Splay\) 系函数

#define Son(x) (x==t[t[x].f].ch[1])

il void Rotate(ri int x){
    ri int y=t[x].f,z=t[y].f,k=Son(x);
    if(NotRoot(y)) t[z].ch[Son(y)]=x;
    if(t[x].ch[!k]) t[t[x].ch[!k]].f=y;
    t[y].ch[k]=t[x].ch[!k],t[x].ch[!k]=y;
    t[y].f=x,t[x].f=z,PushUp(y);
}

il void Splay(ri int x){
    Update(x);
    for(ri int fa;fa=t[x].f,NotRoot(x);Rotate(x)){
        if(NotRoot(fa)) Rotate(Son(fa)^Son(x)?x:fa);
    }
    PushUp(x);
}

新操作

  1. Access(x) 把从根到 \(x\) 的所有点放在一条实链里,使根到 \(x\) 成为一条实路径,并且在同一棵 \(Splay\) 里。只有此操作是必须实现的,其他操作视题目而实现。
  2. IsRoot(x)/NotRoot(x) 判断 \(x\) 是否是所在树的根。
  3. Reverse(x) 交换 \(x\) 的子树。
  4. Update(x) 在 \(Access\) 操作之后,递归地从上到下 \(PushDown\) 更新信息。
  5. MakeRoot(x) 使 \(x\) 点成为其所在树的根。
  6. Link(x, y) 在 \(x, y\) 两点间连一条边。
  7. Cut(x, y) 把 \(x, y\) 两点间边删掉。
  8. FindRoot(x) 找到 \(x\) 所在树的根节点编号。
  9. Fix(x, v) 修改 \(x\) 的点权为 \(v\)。
  10. Split(x, y) 提取出 \(x, y\) 间的路径,方便做区间操作。

\(Access\)

il void Access(ri int x){
    for(ri int y=0;x;x=t[y=x].f){
        Splay(x),t[x].ch[1]=y,PushUp(x);
    }
}

\(IsRoot/NotRoot\)

#define IsRoot(x) ((x^t[t[x].f].ch[0])&&(x^t[t[x].f].ch[1]))

由于总是用到!IsRoot,所以直接写NotRoot

#define NotRoot(x) (t[t[x].f].ch[0]==x||t[t[x].f].ch[1]==x)

\(Reverse\)

il void Reverse(ri int x){
    swap(t[x].ch[0],t[x].ch[1]);
    t[x].lz^=1;
}

\(Update\)

il void Update(ri int x){
    if(NotRoot(x)) Update(t[x].f);
    PushDown(x);
}

\(MakeRoot\)

il void MakeRoot(ri int x){
    Access(x),Splay(x),Reverse(x);
}

\(Link\)

il void Link(ri int x,ri int y){
    MakeRoot(x);
    if(FindRoot(y)!=x)t[x].f=y;
}

\(Cut\)

il void Cut(ri int x,ri int y){
    MakeRoot(x);
    if(FindRoot(y)==x&&t[y].f==x&&!t[y].ch[0]){
        t[y].f=t[x].ch[1]=0,PushUp(x);
    }
}

\(FindRoot\)

il int FindRoot(ri int x){
    Access(x),Splay(x);
    while(t[x].ch[0]) PushDown(x),x=t[x].ch[0];
    Splay(x);  return x;
}

\(Fix\)

il void Fix(int x,int y){
    Splay(x),t[x].val=y;
}

\(Split\)

il void Split(ri int x,ri int y){
    MakeRoot(x),Access(y),Splay(y);
}
AC·code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;

namespace Q{
    il int rd(){
        ri int x=0;ri bool f=0;ri char c=getchar();
        while(!isdigit(c)) f|=(c==45),c=getchar();
        while(isdigit(c)) x=x*10+(c^48),c=getchar();
        return f?-x:x;
    }
    il void wt(int x){
        if(x<0) x=-x,putchar(45);
        if(x>=10) wt(x/10);
        return putchar(x%10|48),void();
    }
} using namespace Q;

cs int N=1e5+5;

namespace Link_Cut_Tree{
    #define NotRoot(x) (t[t[x].f].ch[0]==x||t[t[x].f].ch[1]==x)
    #define Son(x) (x==t[t[x].f].ch[1])

    struct tree{
        int f,ch[2],val,sum,lz;
    }t[N];

    il void Reverse(ri int x){
        swap(t[x].ch[0],t[x].ch[1]);
        t[x].lz^=1;
    }

    il void PushDown(ri int x){
        if(t[x].lz){
            if(t[x].ch[0]) Reverse(t[x].ch[0]);
            if(t[x].ch[1]) Reverse(t[x].ch[1]);
            t[x].lz=0;
        }
    }
    
    il void Update(ri int x){
        if(NotRoot(x)) Update(t[x].f);
        PushDown(x);
    }

    il void PushUp(ri int x){
        t[x].sum=t[t[x].ch[0]].sum^t[t[x].ch[1]].sum^t[x].val;
    }

    il void Rotate(ri int x){
        ri int y=t[x].f,z=t[y].f,k=Son(x);
        if(NotRoot(y)) t[z].ch[Son(y)]=x;
        if(t[x].ch[!k]) t[t[x].ch[!k]].f=y;
        t[y].ch[k]=t[x].ch[!k],t[x].ch[!k]=y;
        t[y].f=x,t[x].f=z,PushUp(y);
    }
    
    il void Splay(ri int x){
        Update(x);
        for(ri int fa;fa=t[x].f,NotRoot(x);Rotate(x)){
            if(NotRoot(fa)) Rotate(Son(fa)^Son(x)?x:fa);
        }
        PushUp(x);
    }

    il void Access(ri int x){
        for(ri int y=0;x;x=t[y=x].f){
            Splay(x),t[x].ch[1]=y,PushUp(x);
        }
    }

    il void MakeRoot(ri int x){
        Access(x),Splay(x),Reverse(x);
    }

    il int FindRoot(ri int x){
        Access(x),Splay(x);
        while(t[x].ch[0]) PushDown(x),x=t[x].ch[0];
        Splay(x);  return x;
    }

    il void Split(ri int x,ri int y){
        MakeRoot(x),Access(y),Splay(y);
    }

    il void Link(ri int x,ri int y){
        MakeRoot(x);
        if(FindRoot(y)!=x)t[x].f=y;
    }

    il void Cut(ri int x,ri int y){
        MakeRoot(x);
        if(FindRoot(y)==x&&t[y].f==x&&!t[y].ch[0]){
            t[y].f=t[x].ch[1]=0,PushUp(x);
        }
    }

    il void Fix(int x,int y){
        Splay(x),t[x].val=y;
    }
} using namespace Link_Cut_Tree;

int main(){
    ri int n=rd(),m=rd();
    for(ri int i=1;i<=n;++i)t[i].val=rd();
    for(ri int i=1,op,x,y;i<=m;++i){
        op=rd(),x=rd(),y=rd();
        switch(op){
            case 0:Split(x,y),wt(t[y].sum),putchar(10);break;
            case 1:Link(x,y);break;
            case 2:Cut(x,y);break;
            case 3:Fix(x,y);break;
        }
    }
    return 0;
}

标签:ch,int,void,Tree,Splay,Cut,Link,il,ri
From: https://www.cnblogs.com/windymoon/p/17185448.html

相关文章

  • flink-connector-mysql-cdc遇到db名包含点号
    不加反引号报错:2023-03-0614:52:21,320ERROR[618][com.ververica.cdc.connectors.mysql.debezium.reader.SnapshotSplitReader.lambda$submitSplit$0(SnapshotSplitRe......
  • 大白话+画图 从源码角度一步步搞懂ArrayList和LinkedList的使用
    1.说说ArrayList1.基本原理ArrayList,原理就是底层基于数组来实现。01.基本原理:数组的长度是固定的,java里面数组都是定长数组,比如数组大小设置为100,此时你不停的往Arra......
  • LinkedList 源码解读
    1.创建 LinkedListList<String>list=newLinkedList<>();list.add("wang");2.构造方法:开起了什么都没有做/***Constructsanemptylist.*/......
  • JJdbcUtils
    packagecom.st.ustils;importjava.io.FileNotFoundException;importjava.io.FileReader;importjava.io.IOException;importjava.io.Reader;importjava.sql.Conn......
  • JdbcUtils
    packagecom.st.utils;importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.sql.State......
  • 基于simulink的自适应PID控制器仿真
    1.算法描述自适应PID控制,是指自适应控制思想与常规PID控制器相结合形成的自适应PID控制或自校正PID控制技术,人们统称为自适应PID控制。最常用的自适应控制算法有:最小方......
  • 基于simulink的自适应PID控制器仿真
    1.算法描述       自适应PID控制,是指自适应控制思想与常规PID控制器相结合形成的自适应PID控制或自校正PID控制技术,人们统称为自适应PID控制。        ......
  • Paper Reading: Robustness metrics for relational query execution plans
    笔记本篇文章的两个核心内容:“threenovelmetricsfortherobustnes”(FlorianWolf等,2018,p.1360):三个健壮性指标(关于基数估计误差),用于衡量qep的健壮性。适用性......
  • docker启动创建容器时,报错Cannot link to /mysql, as it does not belong to the defa
    启动创建容器时,报错Cannotlinkto/mysql,asitdoesnotbelongtothedefaultnetwork从报错信息看是不属于默认网络分析容器网络通过dockerinspect容器id先......
  • KDTree实现KNN算法
    KDTree实现KNN算法完整的实验代码在我的github上......