[模板] 动态树/LCT

简介

LCT是一种数据结构, 可以维护树的动态加边, 删边, 维护链上信息(满足结合律), 单次操作时间复杂度 \(O(\log n)\).(不会证)

思想类似树链剖分, 因为splay可以换根, 用splay维护重链, splay的中序遍历即为链按深度从小到大遍历的结果.

注意区分splay和整棵树的区别, splay根和树根的区别.

\(Access(p)\) 操作指的是将p与根放在同一棵splay中.

\(MakeRoot(p)\) 操作指的是将p变为它所在树(而不是splay)的根. 由于维护的是链上信息, 这不会对答案产生影响.

\(FindRoot(p)\) 操作指的是求p所在树(而不是splay)的根.

\(Link(x,y)\) 操作指的是如果p与q不在同一棵树中, 那么连边 \((x,y)\).

\(Cut(x,y)\) 操作指的是如果p与q有边相连, 那么连边 \((x,y)\).
其中, ‘p与q有边相连‘ 等价于同时满足:
- p,q在同一棵树中.
- p,q深度相差1.(splay中p,q中序遍历时相邻)

\(Split(x,y)\) 操作指的是取出 \((x,y)\) 这个链, 并放在一棵splay中.

具体实现见代码.

Code

const int nsz=3e5+50;
int n,val[nsz];
struct tlct{
    struct tnd{int ch[2],fa,sum,rv;}tree[nsz];
#define ls(p) tree[p].ch[0]
#define rs(p) tree[p].ch[1]
#define fa(p) tree[p].fa
#define dir(p) (rs(fa(p))==p)
    bool isrt(int p){return ls(fa(p))!=p&&rs(fa(p))!=p;}//splay rt
    void rev(int p){swap(ls(p),rs(p));tree[p].rv^=1;}
    void pu(int p){
        tree[p].sum=tree[ls(p)].sum^val[p]^tree[rs(p)].sum;
    }
    void pd(int p){
        if(tree[p].rv==0)return;
        if(ls(p))rev(ls(p));
        if(rs(p))rev(rs(p));
        tree[p].rv=0;
    }
    void pdt(int p){//push down whole splay
        if(!isrt(p))pdt(fa(p));
        pd(p);
    }
    void rotate(int p){//fa(p) should exist
        int x=fa(p),y=fa(x),dir1=dir(p),dir2=dir(x),z=tree[p].ch[dir1^1];
        if(!isrt(x))tree[y].ch[dir2]=p; fa(p)=y;
        tree[p].ch[dir1^1]=x;           fa(x)=p;
        tree[x].ch[dir1]=z;             if(z)fa(z)=x;
        pu(x);
        pu(p);
    }

    void splay(int p){
        pdt(p);
        while(!isrt(p)){
            if(!isrt(fa(p)))rotate(dir(p)==dir(fa(p))?fa(p):p);
            rotate(p);
        }
    }

    void access(int p){
        for(int y=0;p;y=p,p=fa(p)){
            splay(p),rs(p)=y;
            pu(p);
        }
    }
    void makert(int p){//p -> tree rt & splay rt
        access(p),splay(p);
        rev(p);
    }
    int findrt(int p){//find tree rt; p -> splay rt
        access(p),splay(p);
        while(ls(p))pd(p),p=ls(p);
        return p;
    }
    bool iscon(int x,int y){return findrt(x)==findrt(y);}
    void link(int x,int y){
        makert(x);
        if(findrt(y)!=x)fa(x)=y;
    }
    void cut(int x,int y){
        makert(x);
        if(findrt(y)==x&&fa(x)==y&&ls(x)==0)
            fa(x)=ls(y)=0,pu(y);
    }
    void split(int x,int y){//y -> splay rt
        makert(x);
        access(y);
        splay(y);
    }
    void pr(){
        rep(i,1,n)printf("nd=%d fa=%d ls=%d rs=%d sum=%d rv=%d\n",i,fa(i),ls(i),rs(i),tree[i].sum,tree[i].rv);
    }
}lct;

//init
rep(i,1,n)tree[i].sum=val[i];

//query a chain
    lct.split(b,c);
    ans=lct.tree[c].sum;

//modify one point
    lct.makert(b);
    val[b]=c;
    lct.pu(b);

原文地址:https://www.cnblogs.com/ubospica/p/10270646.html

时间: 2024-10-28 23:41:49

[模板] 动态树/LCT的相关文章

hdu 5002 (动态树lct)

Tree Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 920    Accepted Submission(s): 388 Problem Description You are given a tree with N nodes which are numbered by integers 1..N. Each node is a

hdu 5398 动态树LCT

GCD Tree Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 415    Accepted Submission(s): 172 Problem Description Teacher Mai has a graph with n vertices numbered from 1 to n. For every edge(u,v),

bzoj2049-洞穴勘测(动态树lct模板题)

Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假如两个洞穴可以通过一条或者多条通道按一定顺序连接起来,那么这两个洞穴就是连通的,按顺序连接在一起的这些通道则被称之为这两个洞穴之间的一条路径.洞穴都十分坚固无法破坏,然而通道不太稳定,时常因为外界影响而发生改变,比如,根据有关仪器的监测结果,123号洞穴和127号洞穴之间有时会出现一条通

动态树LCT小结

最开始看动态树不知道找了多少资料,总感觉不能完全理解.但其实理解了就是那么一回事...动态树在某种意思上来说跟树链剖分很相似,都是为了解决序列问题,树链剖分由于树的形态是不变的,所以可以通过预处理节点间的关系,将树转化成连续的区间,再加以其它的数据结构,便能以较快的速度处理序列的修改和查询. 而动态树的问题,是包括了树的合并和拆分操作.这个时候,通过预处理实现的静态树的序列算法不能满足我们的要求,于是我们需要一颗‘动态’的树,能在O(logN)的时间复杂度,处理所有操作. Splay实现的Lin

HDU 5002 Tree(动态树LCT)(2014 ACM/ICPC Asia Regional Anshan Online)

Problem Description You are given a tree with N nodes which are numbered by integers 1..N. Each node is associated with an integer as the weight. Your task is to deal with M operations of 4 types: 1.Delete an edge (x, y) from the tree, and then add a

bzoj2243-染色(动态树lct)

解析:增加三个变量lc(最左边的颜色),rc(最右边的颜色),sum(连续相同颜色区间段数).然后就是区间合并的搞法.我就不详细解释了,估计你已经想到 如何做了. 代码 #include<cstdio> #include<cstring> #include<string> #include<vector> #include<algorithm> using namespace std; const int maxn=100005; int N,M

动态树 LCT

bzoj2049 洞穴探测 题目大意:lct(link,cut,判联通). #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define maxnode 10005 using namespace std; struct lct{ int fa[maxnode],ch[maxnode][2],rev[maxnode],zh[maxnode]; void in

动态树LCT

1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=100000+10,INF=-1u>>1; 7 int v[maxn],maxv[maxn],minv[maxn],sumv[maxn],ch[maxn][2],pre[maxn],top[maxn],flip

luoguP3690 【模板】Link Cut Tree (动态树)[LCT]

题目背景 动态树 题目描述 给定N个点以及每个点的权值,要你处理接下来的M个操作.操作有4种.操作从0到3编号.点从1到N编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和.保证x到y是联通的. 1:后接两个整数(x,y),代表连接x到y,若x到Y已经联通则无需连接. 2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在. 3:后接两个整数(x,y),代表将点X上的权值变成Y. 输入输出格式 输入格式: 第1行两个整数,分别为N和M,代表点数和操