LCT

\(LCT\),全称 \(Link\) \(Cut\) \(Tree\),可以解决动态树问题。

动态树(LCT)

\(isroot\) 操作

如果一个点不是根,则考虑它既不是父亲的左儿子也不是父亲的右儿子。代码:

bool isroot(int x){
    return tr[tr[x].p].s[0]!=x&&tr[tr[x].p].s[1]!=x;
}

\(rotate\) 操作

假设 \(x\) 为 \(7\),\(y\) 为 \(3\),\(z\) 为 \(1\),也就是说要把 \(7\) 旋上去。考虑旋上去的话,就把父亲转到自己下面。于是先断开与父亲的边,连上与爷爷的边。

接下来发现父亲少了一个儿子,于是把自己的另外一边的儿子给父亲,同时把他连接自己的边断掉。

最后我们只需要链接 \(x\) 和 \(y\),只不过这次 \(x\) 是父亲。

代码:

void rotate(int x){
    int y=tr[x].p,z=tr[y].p;
    int k=tr[y].s[1]==x;
    if(!isroot(y))tr[z].s[tr[z].s[1]==y]=x;
    tr[x].p=z;
    tr[y].s[k]=tr[x].s[k^1];
    tr[tr[x].s[k^1]].p=y;
    tr[x].s[k^1]=y;
    tr[y].p=x;
    pushup(y);
    pushup(x);
}

\(splay\) 操作

先分类讨论一下,看 \(x,y,z\) 在不在一条直线上,可以看一下图:

不难发现,如果在一条直线上,则先转 \(y\),再转 \(x\);否则连着转两下 \(x\)。这样一直操作直到 \(x\) 被转到跟上。

但是在进行旋转之前,我们要先把转上去的路径做个 \(pushdown\)。代码:

void splay(int x){
    int top=0,r=x;
    stk[++top]=r;
    while(!isroot(r))stk[++top]=r=tr[r].p;
    while(top)pushdown(stk[top--]);
    while(!isroot(x)){
        int y=tr[x].p,z=tr[y].p;
        if(!isroot(y)){
            if((tr[y].s[1]==x)^(tr[z].s[1]==y))rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
}