zoukankan      html  css  js  c++  java
  • LCT(1)

    LCT(Link-Cut Tree,动态树)是一个支持动态修改树的结构的数据结构,其基本操作有 ( exttt{access}) , ( exttt{findroot}) , ( exttt{makeroot}), 其核心操作有 ( exttt{link})( exttt{cut}) .

    本文具体讲述 LCT 的操作内容和实际运用,不深入讨论如何得到每个具体操作。

    1. LCT 简介

    这里用到树链剖分的思想。LCT 的本质是动态维护链剖分。我们将树链剖分,分为实链和虚链,每一条实链用辅助树即平衡树(选择splay)来维护链上信息,每一条虚链连接各个辅助树,每棵辅助树按照深度为关键字维护。每次我们动态进行实链剖分来完成对树上信息和结构的操作。

    2. LCT 的实现

    接下来介绍 LCT 的各种操作:

    2.1. ACCESS

    ( exttt{access(x)}) 是 LCT 最基本的操作,功能为将根到 (x) 的一条链变为实链。这样的用途当然是将这段路径放入一棵辅助树,方便进行操作。

    这里不再手玩,直接给出操作过程 (设 (y)(x) 新实链上的儿子):

    1. (x) 旋转到当前辅助树的根
    2. (x) 辅助树中的右儿子改为 (y)
    3. (y leftarrow x, xleftarrow fa[x]) ,向上递归操作。

    具体可以这样理解:将 (x) splay 后,他的右儿子比 (x) 的深度大,即为一条实链,那我们就断掉这条实链,把 (y) 接上去,具体实现即为直接修改右儿子。

    for(int y=0;x;y=x,x=fa[x]) splay(x),ch[x][1]=y,pushup(x);
    

    2.2. MAKEROOT

    ( exttt{makeroot(x)}) 即将 (x) 提到原树的根。具体操作:我们 ( exttt{access(x),splay(x)}) 后,(x) 变为当前辅助树的根,且由于其深度最大,所以没有右子树。然后我们把左右子树翻转,这样就没有比 (x) 深度更小的点了, (x) 就变成根啦。

    access(x),splay(x),rev[x]^=1;
    

    2.3. FINDROOT

    ( exttt{findroot(x)}) 即为找 (x) 所在原树的树根(注意:LCT维护的不一定是一棵树,可能是森林!)。

    具体操作: ( exttt{access(x),splay(x)}) 后,一路走到最左边即可。很好理解。当然要记得 ( exttt{pushdown}) .

    特别注意:最后要 splay(x) 保证复杂度!!!

    access(x),splay(x); int y=x;
    while(ch[y][0]) pushdown(y),y=ch[y][0];
    splay(x); return y;
    

    (PS:这可能是LCT基本操作里最长的函数了……)
    (PS:在没有 cut 操作的题目可能用并查集更简单)

    ( exttt{link(x,y)}) 即为连接两个节点。直接 ( exttt{makeroot(x)}) 后将 (x) 的父亲变为 (y) 即可。

    makeroot(x),fa[x]=y;
    

    2.5. SPLIT

    不算是基本操作,但也很常用且简单。 ( exttt{split(x,y)}) 即为将 (x ightarrow y) 的一条路径拉出来。我们将 (x) 置为根后 ( exttt{access(y)}) 即可。再加上 ( exttt{splay(y)}) 即可直接操作 (y)(x) 即为 (y) 的左儿子。

    makeroot(x),access(y),splay(y);
    

    2.6. CUT

    ( exttt{cut(x,y)}) 即为断掉 (x ightarrow y) 的这条边。 ( exttt{split(x,y)}) 之后直接将 (fa[x])(ch[y][0]) 置为 (0) 即可。

    split(x,y),fa[x]=ch[y][0]=0;
    

    3. 辅助树

    辅助树之前说过了,用splay来实现。接下来具体讲一讲 LCT 中的 splay 中的变化。

    这里先直接贴代码:

    #define isnrt(x) (ch[fa[x]][0]==x||ch[fa[x]][1]==x)
    void pushdown(int x)
    {
    	if(rev[x])
    	{
    		rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
    		swap(ch[x][0],ch[x][1]);
    		rev[x]=0;
    	}
    }
    void rotate(int x){
    	int y=fa[x],z=fa[y];
    	bool k=ch[y][0]==x; int w=ch[x][k];
    	if(isnrt(y))ch[z][ch[z][1]==y]=x;ch[x][k]=y;ch[y][!k]=w;
    	fa[w]=y;fa[y]=x;fa[x]=z;
    }
    void splay(int x)
    {
    	int y=x,tp=1; st[1]=y;
    	while(isnrt(y)) st[++tp]=y=fa[y];
    	while(tp) pushdown(st[tp--]);
    	while(isnrt(x))
    	{
    		int y=fa[x],z=fa[y];
    		if(isnrt(y)) (ch[z][1]==y)^(ch[y][1]==x)?rotate(x):rotate(y);
    		rotate(x);
    	}
    }
    

    变化很容易看出:当前辅助树的根需要特判(代码中用 ( exttt{isnrt(x)}) 来实现)。具体的不再解释了。同时注意 ( exttt{pushdown}) .

    4. 模板

    模板题:给你一棵树,每个节点上一个权值,支持加边删边,修改单点权值,查询路径 ( exttt{xor}) 和 .

    代码:略。

    关于 LCT 的具体用途和题目,在(2)中详细讲解。

  • 相关阅读:
    Opencv在mac系统的安装与试用
    VINS 估计器之检查视差
    C语言——第零次作业
    C语言博客05指针
    循环结构
    C语言博客作业数组
    函数3
    C博客作业01分支、顺序结构
    group by的查询
    layui多张图片上传最多9张(新增和修改时的显示问题)
  • 原文地址:https://www.cnblogs.com/farway17/p/10425527.html
Copyright © 2011-2022 走看看