芝士:LCT

LCT

主要用来解决动态树的问题

基本思路

考虑为什么树链剖分的时间复杂度如此优秀?其原因在于将点分成几个部分,每一个部分用一个数据结构来维护,同时保证分成的这几个部分只需要用\(log\)个部分就能构造出树上所有的路径,使每次的操作的时间复杂度大大减小

\(LCT\)也是如此,考虑用一颗splay来维护树的结构,我们用splay维护一条从上往下的链,定义键值为每个点的深度,将原始的树划分成几个链之后,你会发现有他们的splay使分开的,所以我们需要将splay的根之间连边,这个边是特殊的:父亲不知道儿子,但是儿子知道父亲。那么这里出现了一个问题,因为splay是可以换根的,换完跟之后原根的父亲怎么维护?其实不需要考虑这么多,考虑这些链在树上的样子,其实splay之间连的边,一定是从一个splay中最小的那个键值连出去的。

核心操作(access)

LCT中有一个很重要的操作\(access(u)\),即让\(u\)和树的根在同一个splay里面,同时需要保证,这个splay中最大的键值是\(u\),这应该很好实现,只需要考虑两个splay的合并,只需要将下面那个的splay中键值最小\(v\)的一个转到根,再把上面那个splay中\(v\)的父亲转到根,再把右子树删去即可,对于剩下的两个splay,合并只需要连边

换根(makeroot)

只需要将\(access(u)\),再整体翻转这个splay

\(makeroot(u)\),再把父亲的信息更改即可

断边(cut)

考虑到一条边连向的两个节点的特殊性,一个深度一定比另一个深度大1

所以只需要将两个节点弄到同一个splay中,再删除就好了

找根节点(findroot)

\(access(u)\),再寻找这个splay中键值最小的这个点

例题

传送门

#include<iostream>
#include<cstdio>
using namespace std;
namespace LCT
{
    struct node
    {
        int lson,rson;
        int x,fa,val;
        int lazy;
    }tre[100005];
    void update(int x)
    {
        tre[x].val=tre[x].x^tre[tre[x].lson].val^tre[tre[x].rson].val;
    }
    void push_down(int x)
    {
        if(tre[x].lazy==0)
            return;
        tre[x].lazy=0;
        tre[tre[x].lson].lazy^=1;
        tre[tre[x].rson].lazy^=1;
        swap(tre[tre[x].lson].lson,tre[tre[x].lson].rson);
        swap(tre[tre[x].rson].lson,tre[tre[x].rson].rson);
    }
    bool isroot(int x)
    {
        if(tre[tre[x].fa].lson!=x&&tre[tre[x].fa].rson!=x)
            return 1;
        return 0;
    }
    void rotate(int x)
    {
        int y=tre[x].fa;
        push_down(y);
        push_down(x);
        if(!isroot(y))
        {
            if(tre[tre[y].fa].lson==y)
                tre[tre[y].fa].lson=x;
            else
                tre[tre[y].fa].rson=x;
        }
        tre[x].fa=tre[y].fa;
        tre[y].fa=x;
        if(tre[y].lson==x)
        {
            tre[y].lson=tre[x].rson;
            tre[tre[x].rson].fa=y;
            tre[x].rson=y;
        }
        else
        {
            tre[y].rson=tre[x].lson;
            tre[tre[x].lson].fa=y;
            tre[x].lson=y;
        }
        update(y);
        update(x);
    }
    void down(int x)
    {
    	if(x==0)
    		return;
        if(isroot(x)==0)
            down(tre[x].fa);
        push_down(x);
    }
    void splay(int x)
    {
        down(x);
        while(!isroot(x))
        {  
            int y=tre[x].fa;
            int z=tre[y].fa;
            if(!isroot(y))
            {
                if((tre[z].lson==y)^(tre[y].lson==x))
                    rotate(x);
                else
                    rotate(y);
            }
            rotate(x);
        }
    }
    void access(int u)
    {
        int v=0;
        while(u)
        {
            splay(u);
            push_down(u);
            tre[u].rson=v;
            tre[v].fa=u;
            update(u);
            v=u;
            u=tre[u].fa;
        }
    }
    void makert(int u)
    {
        access(u);
        splay(u);
        tre[u].lazy^=1;
        swap(tre[u].lson,tre[u].rson);
    }
    int findrt(int u)
    {
        access(u);
		splay(u);
        while(1)
        {
            push_down(u);
            if(tre[u].lson==0)
            {
                splay(u);
                return u;
            }
            u=tre[u].lson;
        }
    }
    int ask(int u,int v)
    {
        makert(u);
        access(v);
        splay(v);
        return tre[v].val;
    }
    void link(int u,int v)
    {
        if(findrt(u)==findrt(v))
            return;
        makert(u);
        tre[u].fa=v;
    }
    void cut(int u,int v)
    {
        makert(u);
        if(u!=findrt(v)||tre[v].fa!=u||tre[v].lson)
            return;
		push_down(u);
        tre[u].rson=0;
        tre[v].fa=0;
        update(v);
        update(u);
    }
    void change(int pos,int x)
    {
        makert(pos);
        tre[pos].x=x;
        update(pos);
    }
}
using namespace LCT;
int n,m;
int opt,u,v;
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>tre[i].x;
		tre[i].val=tre[i].x; 
	}
    for(int i=1;i<=m;i++)
    {  
        cin>>opt>>u>>v;
        if(opt==0)
            cout<<ask(u,v)<<'\n';
        if(opt==1)
            link(u,v);
        if(opt==2)
            cut(u,v);
        if(opt==3)
            change(u,v);
    }
    return 0;
}
posted @ 2020-01-10 21:41  loney_s  阅读(182)  评论(0)    收藏  举报