Loading

LCT

因为博主很懒,所以这里只是简单介绍 LCT 的原理,并不会配图讲解,主要是为了帮助博主日后理解 LCT,所以初学者请另找博客观看。

LCT 有一种树链剖分,分为两种链,实链和虚链,注意这个链是实是虚使我们认为规定的,也就是说我们从每个节点的向下连边中选出一条边来变成实边,其余边为虚边。注意在实际操作中,可能有的节点向下连边全是虚边,不过这种情况仅当这个节点向下连边只有一条时才能够出现,

我们考虑用 LCT 来维护一个森林,我们支持在两个节点之间连边,询问链信息,断掉两个节点之间的边。

我们引入辅助树,对每条实链,我们都用一个 Splay 去维护,这颗 Splay 中序遍历后是这条实链按照深度从小到大,我们考虑虚边就是若干 Splay 之间的的边,我们这样来连接,设原来有一条虚边为 \((x,y)\),那么我们就把 \(x\) 所在 Splay 的根节点的父亲置为 \(y\),但是这里认父不认子,实边是认子认父,也即是 Splay 上的边。

我们首先可以有每个 Splay 中的 rotate 和 Splay 操作,这个好实现,只需要注意我们不要把轻边父亲那过来左旋,注意这一点只需要维护一个是否为根的函数,如果一个节点为根,那么它的父亲不会认它。

我们还可以实现把一个节点通过改变边的实虚来把一个节点连到根节点,具体来说,我们把当前点弄到根上,然后断掉其右儿子,找到父亲,把其右儿子设为当前点,不断重复这样的操作。

容易观察到,这样做完之后,这条链上深度最大的点就是我们操作的这个点,所以我们同样可以利用这个操作来剖路径。

我们还可以指定一个节点为根,这样配合上面的操作,就可以进行剖路径。具体来说,我们把这个节点和当前根相连,容易发现,我们连完之后操作的最后一个点就是根,如果我们旋转其所有的左右儿子,深度最大的点就会变成深度最小的点,这样整棵树的根就变成了我们所需要的。

我们可以剖一个路径出来变成一个 Splay,这样路径操作就可以通过打标机来完成。

Link 和 Cut 操作,首先我们可以实现找到当前树的根,这个通过上面的操作是很好实现的。

Link 操作,只需要把一个弄为当前树的根和 Splay 的根之后直接像另一个点连虚边,这些 Splay 对虚边的条数是没有限制的。

Cut 操作,只需要判断边是否存在后直接割掉边就可以,注意 PushUp。

执行 Find 操作,也就是找根最后一定要记住 Splay 根节点,目的是为了保证复杂度。


#include<bits/stdc++.h>
#define mset(a,b) memset((a),(b),sizeof((a)))
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define dec(i,l,r) for(int i=(r);i>=(l);i--)
#define inc(a,b) (((a)+(b))>=mod?(a)+(b)-mod:(a)+(b))
#define sub(a,b) (((a)-(b))<0?(a)-(b)+mod:(a)-(b))
#define mul(a,b) 1ll*(a)*(b)%mod
#define sgn(a) (((a)&1)?(mod-1):1)
#define cmax(a,b) (((a)<(b))?(a=b):(a))
#define cmin(a,b) (((a)>(b))?(a=b):(a))
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define N 100010
#define M number
using namespace std;

typedef double dd;
typedef long double ld;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
//#define int long long
typedef pair<int,int> P;
typedef vector<int> vi;

const int INF=0x3f3f3f3f;
const dd eps=1e-9;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int tag[N],val[N],ch[N][2],fa[N],n,m,a[N];

inline void Print(){
    rep(i,1,n){
        printf("k=%d tag=%d val=%d ch[0]=%d ch[1]=%d fa=%d\n",i,tag[i],val[i],ch[i][0],ch[i][1],fa[i]);
    }
}

#define ls(k) ch[k][0]
#define rs(k) ch[k][1]
struct LCT{
    inline int Get(int k){return rs(fa[k])==k;}
    inline void PushUp(int k){
        val[k]=a[k]^val[ls(k)]^val[rs(k)];
    }
    inline void C(int k){
        swap(ls(k),rs(k));tag[k]^=1;
    }
    inline void PushDown(int k){
        if(tag[k]){
            C(ls(k));C(rs(k));tag[k]^=1;;
        }
    }
    inline bool Root(int k){
        int fat=fa[k];
        return ls(fat)!=k&&rs(fat)!=k;
    }
    inline void rotate(int k){
        int y=fa[k];int z=fa[y],x=Get(k);
        if(!Root(y)) ch[z][Get(y)]=k;
        ch[y][x]=ch[k][x^1];fa[ch[k][x^1]]=y;
        ch[k][x^1]=y;fa[y]=k;fa[k]=z;
        PushUp(y);PushUp(k);
    }
    inline void Update(int k){
        if(!Root(k)) Update(fa[k]);
        PushDown(k);
    }
    inline void Splay(int k){
        Update(k);
        for(int f=fa[k];!Root(k);rotate(k),f=fa[k]){
            if(!Root(f)) rotate(Get(k)==Get(f)?f:k);
        }
    }
    inline int Access(int k){
        int p;
        for(p=0;k;p=k,k=fa[k]){
            Splay(k);rs(k)=p;PushUp(k);
        }
        return p;
    }
    inline void MakeRoot(int k){
        k=Access(k);C(k);
    }
    inline void Split(int x,int y){
        MakeRoot(x);Access(y);Splay(y);
    }
    inline int Find(int k){
        Access(k);Splay(k);PushDown(k);
        while(ls(k)) k=ls(k),PushDown(k);
        Splay(k);return k;
    }
    inline void Cut(int a,int b){
        MakeRoot(a);
        if(Find(b)==a&&rs(a)==b&&ls(b)==0){
            rs(a)=fa[b]=0;PushUp(a);
        }
    }
    inline void Link(int a,int b){
        MakeRoot(a);if(Find(b)==a) return;Splay(a);fa[a]=b;
    }
    inline void Change(int k,int x){
        MakeRoot(k);a[k]=x;PushUp(k);
    }
    inline int Ask(int a,int b){
        Split(a,b);return val[b];
    }
}lct;

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(m);
    rep(i,1,n){
        read(val[i]);a[i]=val[i];
    }
    rep(i,1,m){
        int op,x,y;read(op);read(x);read(y);
        if(op==0){
            printf("%d\n",lct.Ask(x,y));
        }
        else if(op==1){
            lct.Link(x,y);
        }
        else if(op==2){
            lct.Cut(x,y);
        }
        else if(op==3){
            lct.Change(x,y);
        }
    }
    return 0;
}

posted @ 2022-03-13 09:19  hyl天梦  阅读(422)  评论(0编辑  收藏  举报