LCT(link-cut tree)

LCT(link-cut tree)

时隔多年,我终于学会了\(LCT\),真的高兴坏了

详细解释都在代码注释中,再不行就看大佬的博客

同样是为了方便自己看,因为自己的板子终究是和别人的不一样

Luogu3690 LCT模板

code(注释)
//LCT中的splay是以深度为大小,满足平衡树性质的,也就是说左边的深度小于右边
//分为实边和虚边,所有在splay中的边都是实边
//虚边是通过splay树根的父亲找到的,虚边只有儿子认爹,爹的儿子中没有虚边对应的儿子
//不要在意splay中的父子关系混乱,因为按照深度排序,操作得当,总能还原原树的边
//每一颗splay连向其他splay的虚边,在原树上都是由当前splay中深度最小的点,指向父亲的
//对于splay的任意子树也满足上一行所述的性质,因为深度满足平衡树性质
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int N=1e5+5;
int n,q;
struct LCT{
    struct POT{
        int sum,val,son[2],fa;
        bool rev;//是否翻转
    }tr[N];
    int pth[N],pt;//到根的路径上需要全部pushdown,用这个东西模拟栈,防止炸掉
    void pushup(int x){
        tr[x].sum=tr[tr[x].son[0]].sum^tr[tr[x].son[1]].sum^tr[x].val;
        return ;
    }
    void pushr(int x){
        //对单独节点翻转儿子,并打上翻转标记,翻转标记只在需要换根的时候需要
        swap(tr[x].son[0],tr[x].son[1]);
        tr[x].rev^=1;
    }
    void pushdown(int x){
        if(!tr[x].rev)return ;
        if(tr[x].son[0])pushr(tr[x].son[0]);
        if(tr[x].son[1])pushr(tr[x].son[1]);
        tr[x].rev=0;return ;
    }
    bool nroot(int x){return tr[tr[x].fa].son[0]==x||tr[tr[x].fa].son[1]==x;}
    //判断是否是当前splay的跟,是的话返回false,因为认爹不认儿子,看看爹有没有自己这个儿子就行了
    int get(int x){return x==tr[tr[x].fa].son[1];}
    void rotate(int x){
        int y=tr[x].fa,z=tr[y].fa;
        int xpos=get(x),ypos=get(y);
        if(nroot(y))tr[z].son[ypos]=x;//不能更改0节点的信息,必须要判断
        tr[x].fa=z;tr[y].fa=x;
        tr[y].son[xpos]=tr[x].son[xpos^1];
        tr[tr[x].son[xpos^1]].fa=y;
        tr[x].son[xpos^1]=y;
        pushup(y);return ;
        //此处不需要pushup(x),因为x会一直跳上去,所以可以在splay()中最后pushup一下就好了
    }
    void splay(int x){
        pt=0;int now=x;pth[++pt]=now;
        while(nroot(now))pth[++pt]=now=tr[now].fa;
        while(pt)pushdown(pth[pt--]);
        //从跟开始一直pushdown下来,手工栈
        while(nroot(x)){
            int y=tr[x].fa,z=tr[y].fa;
            int xpos=get(x),ypos=get(y);
            if(nroot(y)){//判断有没有爹
                if(xpos==ypos)rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        pushup(x);
    }
    void access(int x){
        //打通一条从原树的跟到x节点的路径,端点是跟和x节点
        //此时当前splay中只有跟到x节点路径上的点
        for(int y=0;x;y=x,x=tr[x].fa)
            splay(x),tr[x].son[1]=y,pushup(x);
    }
    void makeroot(int x){//让x成为原树的树根
        access(x);splay(x);pushr(x);
        //此时需要翻转区间,深度翻转,因为本来x是最低的,现在是最高的,直接整体翻转就行了
        //并且此时x一定没有右儿子,因为在这一颗splay中它是深度最低的点
    }
    int findroot(int x){
        //找到原树的根,先把x到跟的路径打通,然后把x转上来,接着就直接往左找
        access(x);splay(x);
        while(tr[x].son[0])pushdown(x),x=tr[x].son[0];
        splay(x);return x;
    }
    void split(int x,int y){
        //提取x到y的路径,并且使路径所在splay根为y
        makeroot(x);access(y);splay(y);
    }
    bool link(int x,int y){
        //连边,返回值为是否链接成功,成功返回true
        makeroot(x);
        if(findroot(y)==x)return false;//判断是不是在一棵树内,在的话不能继续连边
        tr[x].fa=y;return true;
        //必须是x向y连边,因为此时x是跟,但是y不一定是
        //此时连的是虚边,不需要pushup
    }
    bool cut(int x,int y){
        //断边,返回值为是否断开成功,成功返回true
        makeroot(x);//这里断开了与y链接的实边,保证要被cut掉的一定是虚边,为下面判断做准备
        if(findroot(y)!=x||tr[y].fa!=x||tr[y].son[0])return false;
        //上面三个条件缺一不可,1、在一棵树内
        //2、y的爹是x。因为此时findroot了,y是所在splay的跟,父亲一定是x
        //3、y的深度减一一定是x,但是现在x是跟,所以y的左儿子不能有东西
        tr[y].fa=tr[x].son[1]=0;pushup(x);
        //有可能少了一个点,必须要pushup,不知道此时的连边是实边还是虚边,所以必须pushup
        return true;
    }
}lct;
signed main(){
    n=read();q=read();
    fo(i,1,n)lct.tr[i].val=read();
    while(q--){
        int tp=read(),x=read(),y=read();
        if(tp==0)lct.split(x,y),printf("%d\n",lct.tr[y].sum);
        if(tp==1)lct.link(x,y);
        if(tp==2)lct.cut(x,y);
        if(tp==3)lct.splay(x),lct.tr[x].val=y,lct.pushup(x);
    }
}
code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int N=1e5+5;
int n,q;
struct LCT{
    struct POT{
        int sum,val,son[2],fa;
        bool rev;
    }tr[N];
    int pth[N],pt;
    void pushup(int x){
        tr[x].sum=tr[tr[x].son[0]].sum^tr[tr[x].son[1]].sum^tr[x].val;
        return ;
    }
    void pushr(int x){
        swap(tr[x].son[0],tr[x].son[1]);
        tr[x].rev^=1;
    }
    void pushdown(int x){
        if(!tr[x].rev)return ;
        if(tr[x].son[0])pushr(tr[x].son[0]);
        if(tr[x].son[1])pushr(tr[x].son[1]);
        tr[x].rev=0;return ;
    }
    bool nroot(int x){return tr[tr[x].fa].son[0]==x||tr[tr[x].fa].son[1]==x;}
    int get(int x){return x==tr[tr[x].fa].son[1];}
    void rotate(int x){
        int y=tr[x].fa,z=tr[y].fa;
        int xpos=get(x),ypos=get(y);
        if(nroot(y))tr[z].son[ypos]=x;
        tr[x].fa=z;tr[y].fa=x;
        tr[y].son[xpos]=tr[x].son[xpos^1];
        tr[tr[x].son[xpos^1]].fa=y;
        tr[x].son[xpos^1]=y;
        pushup(y);return ;
    }
    void splay(int x){
        pt=0;int now=x;pth[++pt]=now;
        while(nroot(now))pth[++pt]=now=tr[now].fa;
        while(pt)pushdown(pth[pt--]);
        while(nroot(x)){
            int y=tr[x].fa,z=tr[y].fa;
            int xpos=get(x),ypos=get(y);
            if(nroot(y)){
                if(xpos==ypos)rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        pushup(x);
    }
    void access(int x){
        for(int y=0;x;y=x,x=tr[x].fa)
            splay(x),tr[x].son[1]=y,pushup(x);
    }
    void makeroot(int x){
        access(x);splay(x);pushr(x);
    }
    int findroot(int x){
        access(x);splay(x);
        while(tr[x].son[0])pushdown(x),x=tr[x].son[0];
        splay(x);return x;
    }
    void split(int x,int y){
        makeroot(x);access(y);splay(y);
    }
    bool link(int x,int y){
        makeroot(x);
        if(findroot(y)==x)return false;
        tr[x].fa=y;return true;
    }
    bool cut(int x,int y){
        makeroot(x);
        if(findroot(y)!=x||tr[y].fa!=x||tr[y].son[0])return false;
        tr[y].fa=tr[x].son[1]=0;pushup(x);
        return true;
    }
}lct;
signed main(){
    n=read();q=read();
    fo(i,1,n)lct.tr[i].val=read();
    while(q--){
        int tp=read(),x=read(),y=read();
        if(tp==0)lct.split(x,y),printf("%d\n",lct.tr[y].sum);
        if(tp==1)lct.link(x,y);
        if(tp==2)lct.cut(x,y);
        if(tp==3)lct.splay(x),lct.tr[x].val=y,lct.pushup(x);
    }
}

关于LCT的活用

一般用\(LCT\)维护链的信息

然而这里面所有操作的基础就是\(access\)

所以当我们无法维护信息的时候,可以试着更改\(access\)操作,有意想不到的效果

例题

Luogu4172 水管局长

这个就是典型的维护边权的题

首先维护边权的话,我们只需要把边也当成点

连边的话,就从这个点向连接的两个点都连一个边,断边同理

\(map\)访问原树两个点之间对应哪个边

这样空间要开两倍,然后注意维护权值的时候判断一下是否是边所对应的点

这个题就是动态维护最小生成树

我们把询问反过来,看做是一条一条的加边

这样如果两个点之间已经有连边了,那就询问权值最大的边

比较当前边和询问的最大值,如果当前边权值更小的话,删掉询问出来的边,加入当前边

code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int inf=0x3f3f3f3f;
const int N=1e6+1e5+5;
int n,m,q;
struct LCT{
    struct POT{
        int mx,id,val,son[2],fa;
        bool rev;
        POT(){val=-inf;}
    }tr[N];
    int pth[N],pt;
    void pushup(int x){
        tr[x].mx=tr[x].val;tr[x].id=x;
        if(tr[tr[x].son[0]].mx>tr[x].mx){
            tr[x].mx=tr[tr[x].son[0]].mx;
            tr[x].id=tr[tr[x].son[0]].id;
        }
        if(tr[tr[x].son[1]].mx>tr[x].mx){
            tr[x].mx=tr[tr[x].son[1]].mx;
            tr[x].id=tr[tr[x].son[1]].id;
        }
        return ;
    }
    void pushr(int x){
        swap(tr[x].son[0],tr[x].son[1]);
        tr[x].rev^=1;
    }
    void pushdown(int x){
        if(tr[x].rev){
            if(tr[x].son[0])pushr(tr[x].son[0]);
            if(tr[x].son[1])pushr(tr[x].son[1]);
            tr[x].rev=0;
        }
        return ;
    }
    bool nroot(int x){return tr[tr[x].fa].son[0]==x||tr[tr[x].fa].son[1]==x;}
    int get(int x){return tr[tr[x].fa].son[1]==x;}
    void rotate(int x){
        int y=tr[x].fa,z=tr[y].fa;
        int xpos=get(x),ypos=get(y);
        if(nroot(y))tr[z].son[ypos]=x;
        tr[x].fa=z;tr[y].fa=x;
        tr[y].son[xpos]=tr[x].son[xpos^1];
        tr[tr[x].son[xpos^1]].fa=y;
        tr[x].son[xpos^1]=y;
        pushup(y);return ;
    }
    void splay(int x){
        int now=x;pth[++pt]=now;
        while(nroot(now))pth[++pt]=now=tr[now].fa;
        while(pt)pushdown(pth[pt--]);
        while(nroot(x)){
            int y=tr[x].fa,z=tr[y].fa;
            int xpos=get(x),ypos=get(y);
            if(nroot(y)){
                if(xpos==ypos)rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        pushup(x);
    }
    void access(int x){
        for(int y=0;x;y=x,x=tr[x].fa)
            splay(x),tr[x].son[1]=y,pushup(x);
    }
    void makeroot(int x){
        access(x);splay(x);pushr(x);
    }
    int findroot(int x){
        access(x);splay(x);
        while(tr[x].son[0])pushdown(x),x=tr[x].son[0];
        splay(x);return x;
    }
    void split(int x,int y){
        makeroot(x);access(y);splay(y);
    }
    bool link(int x,int y){
        makeroot(x);
        if(findroot(y)==x)return false;
        tr[x].fa=y;return true;
    }
    bool cut(int x,int y){
        makeroot(x);
        if(findroot(y)!=x||tr[y].fa!=x||tr[y].son[0])return false;
        tr[y].fa=tr[x].son[1]=0;pushup(x);return true; 
    }
    bool lt(int x,int y){
        makeroot(x);
        if(findroot(y)==x)return true;
        else return false;
    }
}lct;
pair<int,int> e[N];
map<pair<int,int>,int> mp;
int val[N];
bool vis[N];
struct QUS{
    int tp,x,y;
}qus[N];
int ans[N],an;
signed main(){
    n=read();m=read();q=read();
    fo(i,1,m){
        int x=read(),y=read();
        e[i]=make_pair(x,y);
        val[i]=read();
        lct.tr[i+n].val=val[i];
        lct.tr[i+n].id=i+n;
        lct.pushup(i+n);
        mp[e[i]]=i;
        swap(e[i].first,e[i].second);
        mp[e[i]]=i;
    }
    fo(i,1,q){
        int tp=read(),x=read(),y=read();
        qus[i]=QUS{tp,x,y};
        if(qus[i].tp==2)vis[mp[make_pair(qus[i].x,qus[i].y)]]=true;
    }
    fo(i,1,m){
        if(vis[i])continue;
        int x=e[i].first,y=e[i].second;
        if(lct.lt(x,y)){
            lct.split(x,y);
            if(lct.tr[y].mx<=val[i])continue;
            else {
                int id=lct.tr[y].id-n;
                lct.cut(e[id].first,id+n);
                lct.cut(e[id].second,id+n);
                lct.link(x,i+n);
                lct.link(y,i+n);
            }
        }
        else {
            lct.link(x,i+n);
            lct.link(y,i+n);
        }
    }
    while(q){
        int x=qus[q].x,y=qus[q].y;
        if(qus[q].tp==1){
            lct.split(x,y);
            ans[++an]=lct.tr[y].mx;
        }
        else {
            int id=mp[make_pair(x,y)];
            lct.split(x,y);
            if(val[id]>=lct.tr[y].mx);
            else {
                int mid=lct.tr[y].id-n;
                lct.cut(e[mid].first,mid+n);
                lct.cut(e[mid].second,mid+n);
                lct.link(x,id+n);
                lct.link(y,id+n);
            }
        }
        q--;
    }
    fu(i,an,1)printf("%d\n",ans[i]);
}

BZOJ3159 决战

两颗\(LCT\)对着跑的感觉真不赖

翻转一条边上的权值,但是不翻转位置

看到第一句话的时候,我们可以立刻马上想到是\(LCT\)

但是看到第二句话的时候,发现好像维护不了啊

确实,但是两颗就行了......

没有什么是两颗解决不了的,两颗线段树,两个堆,两颗平衡树......

我们用一颗维护树的形态,也就是我们平时写的\(LCT\)

另一颗......其实也不能叫他\(LCT\)因为两颗\(splay\)之间是没有联系的,也就是说没有虚边

就叫他小树林吧......

小树林里\(splay\)的联通状态是一样的,也就说中序遍历之后和形态树是一一对应的

我们也就是这样维护每一个点的权值的

于是我们有一些地方写的时候有一点小差距

我们在形态树中定义\(rt\)为当前点对应的小树林中的树的树根是哪个

于是我们在\(splay\)后要把根也变一下,注意这个根要实时变化,因为我们的操作太多了

不断地\(findrt\),这个是找每一颗\(splay\)的根

\(access\)的时候,注意带着小树林里的树连通性一起变,很麻烦......

code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
inline int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int inf=1e18;
const int N=5e4+5;
struct V{
    struct POT{
        int sum,mx,mn,val,siz,tag;
        int son[2],fa;
        bool rev;
        POT(){mx=-inf;mn=100000000;}
    }tr[N];
    int pth[N],pt;
    void init(int x){tr[x].val=tr[x].sum=tr[x].mn=tr[x].mx=0;tr[x].siz=1;}
    void pushup(int x){
        // if(x==0)return ;
        tr[x].siz=tr[tr[x].son[0]].siz+tr[tr[x].son[1]].siz+1;
        tr[x].sum=tr[tr[x].son[0]].sum+tr[tr[x].son[1]].sum+tr[x].val;
        tr[x].mx=max(tr[tr[x].son[0]].mx,max(tr[tr[x].son[1]].mx,tr[x].val));
        tr[x].mn=min(tr[tr[x].son[0]].mn,min(tr[tr[x].son[1]].mn,tr[x].val));
        return ;
    }
    void pushr(int x){
        swap(tr[x].son[0],tr[x].son[1]);
        tr[x].rev^=1;
    }
    void pusht(int x,int c){
        tr[x].sum+=tr[x].siz*c;
        tr[x].val+=c;tr[x].tag+=c;
        tr[x].mn+=c;tr[x].mx+=c;
        return ;
    }
    void pushdown(int x){
        if(tr[x].rev){
            if(tr[x].son[0])pushr(tr[x].son[0]);
            if(tr[x].son[1])pushr(tr[x].son[1]);
            tr[x].rev=0;
        }
        if(tr[x].tag){
            if(tr[x].son[0])pusht(tr[x].son[0],tr[x].tag);
            if(tr[x].son[1])pusht(tr[x].son[1],tr[x].tag);
            tr[x].tag=0;
        }
        return ;
    }
    bool nroot(int x){return tr[tr[x].fa].son[0]==x||tr[tr[x].fa].son[1]==x;}
    int get(int x){return tr[tr[x].fa].son[1]==x;}
    void rotate(int x){
        int y=tr[x].fa,z=tr[y].fa;
        int xpos=get(x),ypos=get(y);
        if(nroot(y))tr[z].son[ypos]=x;
        tr[x].fa=z;tr[y].fa=x;
        tr[y].son[xpos]=tr[x].son[xpos^1];
        tr[tr[x].son[xpos^1]].fa=y;
        tr[x].son[xpos^1]=y;
        pushup(y);pushup(x);return ;
    }
    void splay(int x){
        int now=x;pth[++pt]=now;
        while(nroot(now))pth[++pt]=now=tr[now].fa;//cout<<now<<" "<<tr[now].fa<<endl;
        // cout<<x<<endl;
        // cout<<"X"<<" "<<x<<endl;
        while(pt)pushdown(pth[pt--]);
        while(nroot(x)){
            // cout<<"ZZ"<<" "<<tr[x].fa<<" "<<x<<" "<<endl;
            int y=tr[x].fa,z=tr[y].fa;
            int xpos=get(x),ypos=get(y);
            if(nroot(y)){
                if(xpos==ypos)rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        pushup(x);
    }
    int findrt(int &x){
        while(tr[x].fa)x=tr[x].fa;
        return x;
    }
    int findrk(int &x,int k){
        while(true){
            pushdown(x);
            if(tr[tr[x].son[0]].siz>=k)x=tr[x].son[0];
            else if(tr[tr[x].son[0]].siz+1==k)break;
            else k-=(tr[tr[x].son[0]].siz+1),x=tr[x].son[1];
        }
        return x;
    }
    void add(int x,int c){pusht(x,c);}
}v;
struct A{
    struct POT{
        int siz,rt;
        int son[2],fa;
        bool rev;
        POT(){}
    }tr[N];
    int pth[N],pt;
    void init(int x){tr[x].siz=1;tr[x].rt=x;}
    void pushup(int x){
        tr[x].siz=tr[tr[x].son[0]].siz+tr[tr[x].son[1]].siz+1;
        return ;
    }
    void pushr(int x){
        swap(tr[x].son[0],tr[x].son[1]);
        tr[x].rev^=1;
    }
    void pushdown(int x){
        if(tr[x].rev){
            if(tr[x].son[0])pushr(tr[x].son[0]);
            if(tr[x].son[1])pushr(tr[x].son[1]);
            tr[x].rev=0;
        }
        return ;
    }
    bool nroot(int x){return tr[tr[x].fa].son[0]==x||tr[tr[x].fa].son[1]==x;}
    int get(int x){return tr[tr[x].fa].son[1]==x;}
    void rotate(int x){
        int y=tr[x].fa,z=tr[y].fa;
        int xpos=get(x),ypos=get(y);
        if(nroot(y))tr[z].son[ypos]=x;
        tr[x].fa=z;tr[y].fa=x;
        tr[y].son[xpos]=tr[x].son[xpos^1];
        tr[tr[x].son[xpos^1]].fa=y;
        tr[x].son[xpos^1]=y;
        pushup(y);pushup(x);return ;
    }
    void splay(int x){
        int now=x;pth[++pt]=now;
        while(nroot(now))pth[++pt]=now=tr[now].fa;
        while(pt)pushdown(pth[pt--]);
        tr[x].rt=tr[now].rt;
        while(nroot(x)){
            int y=tr[x].fa,z=tr[y].fa;
            int xpos=get(x),ypos=get(y);
            if(nroot(y)){
                if(xpos==ypos)rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        pushup(x);
    }
    void access(int x){
        for(int y=0;x;y=x,x=tr[x].fa){
            splay(x);//cout<<"X"<<" "<<x<<" "<<tr[x].rt<<endl;
            int xx=v.findrt(tr[x].rt),yy=v.findrt(tr[y].rt);
            // if(tr[x].siz!=v.tr[xx].siz)cout<<"SB"<<endl;
            // cout<<"XX"<<" "<<xx<<endl;
            // cout<<"p"<<endl;
            if(!y)yy=0;
            // if(v.tr[xx].siz<tr[tr[x].son[0]].siz+1)cout<<"siz"<<" "<<v.tr[xx].siz<<" "<<tr[tr[x].son[0]].siz+1<<endl;
            xx=v.findrk(xx,tr[tr[x].son[0]].siz+1);
            // cout<<"P"<<endl;
            v.splay(xx);tr[x].rt=xx;
            // cout<<"K"<<endl;
            tr[tr[x].son[1]].rt=v.tr[xx].son[1];
            v.tr[v.tr[xx].son[1]].fa=0;
            v.tr[yy].fa=xx;
            // if(yy==xx)cout<<"you are shit"<<" "<<x<<" "<<tr[y].fa<<" "<<tr[x].son[0]<<" "<<tr[x].son[1]<<" "<<y<<" "<<xx<<" "<<yy<<endl;
            v.tr[xx].son[1]=yy;v.pushup(xx);
            tr[x].son[1]=y;pushup(x);
        }//v.tr[tr[x].rt].fa=0;
    }
    void makeroot(int x){
        access(x);splay(x);
        v.splay(tr[x].rt);
        // cout<<"siz"<<" "<<tr[x].siz<<" "<<v.tr[tr[x].rt].siz<<endl;
        pushr(x);v.pushr(v.findrt(tr[x].rt));
    }
    void split(int x,int y){
        makeroot(x);
        access(y);
        // cout<<"f"<<" "<<y<<" "<<tr[y].rt<<endl;
        splay(y);
        // cout<<"a"<<endl;
        v.splay(v.findrt(tr[y].rt));
    }
    void link(int x,int y){
        makeroot(x);
        tr[x].fa=y;
    }
}a;
int n,q;
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
    to[++rp]=y;
    nxt[rp]=head[x];
    head[x]=rp;
}
void dfs(int x,int f){
    a.tr[x].fa=f;
    for(int i=head[x];i;i=nxt[i]){
        if(to[i]==f)continue;
        dfs(to[i],x);
    }
}
signed main(){
    n=read();q=read();int sb=read();
    fo(i,1,n){
        a.init(i);v.init(i);
    }
    fo(i,1,n-1)a.link(read(),read());
    // fo(i,1,n-1){
    //     int x=read(),y=read();
    //     add_edg(x,y);add_edg(y,x);
    // }
    // dfs(sb,0);
    // cout<<"ZZ"<<endl;
    while(q--){
        char tp[10];scanf("%s",tp+1);
        int x=read(),y=read(),c;
        // cout<<q<<endl;
        a.split(x,y);
        // cout<<"get"<<endl;
        a.tr[y].rt=v.findrt(a.tr[y].rt);
        // cout<<"SB"<<endl;
        //a.tr[y].rt=v.findrt(a.tr[y].rt);
        if(tp[1]=='I'&&tp[3]=='c')c=read(),v.add(a.tr[y].rt,c);
        if(tp[1]=='S')printf("%lld\n",v.tr[a.tr[y].rt].sum);
        if(tp[1]=='M'&&tp[2]=='a')printf("%lld\n",v.tr[a.tr[y].rt].mx);
        if(tp[1]=='M'&&tp[2]=='i')printf("%lld\n",v.tr[a.tr[y].rt].mn);
        if(tp[1]=='I'&&tp[3]=='v')v.pushr(a.tr[y].rt);
        // a.pushup(y);
        // v.pushup(a.tr[y].rt);
    }
}

Luogu2387 NOI2014 魔法森林

两个权值,那就先给其中一个排序

然后维护另一个,用啥,当然是\(LCT\)

于是我们一边插边一边统计答案,看到\(b\)小的就插进去,对了先给\(a\)排序

于是就完了,这个题

code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int N=1e5+5e4+5;
struct LCT{
    struct POT{
        int mx,id,val,son[2],fa;
        bool rev;
    }tr[N];
    int pth[N],pt;
    void pushup(int x){
        tr[x].mx=tr[x].val;tr[x].id=x;
        if(tr[tr[x].son[0]].mx>tr[x].mx){
            tr[x].mx=tr[tr[x].son[0]].mx;
            tr[x].id=tr[tr[x].son[0]].id;
        }
        if(tr[tr[x].son[1]].mx>tr[x].mx){
            tr[x].mx=tr[tr[x].son[1]].mx;
            tr[x].id=tr[tr[x].son[1]].id;
        }
        return ;
    }
    void pushr(int x){
        swap(tr[x].son[0],tr[x].son[1]);
        tr[x].rev^=1;
        return ;
    }
    void pushdown(int x){
        if(tr[x].rev){
            if(tr[x].son[0])pushr(tr[x].son[0]);
            if(tr[x].son[1])pushr(tr[x].son[1]);
            tr[x].rev=0;
        }
        return ;
    }
    bool nroot(int x){return tr[tr[x].fa].son[0]==x||tr[tr[x].fa].son[1]==x;}
    int get(int x){return tr[tr[x].fa].son[1]==x;}
    void rotate(int x){
        int y=tr[x].fa,z=tr[y].fa;
        int xpos=get(x),ypos=get(y);
        if(nroot(y))tr[z].son[ypos]=x;
        tr[x].fa=z;tr[y].fa=x;
        tr[y].son[xpos]=tr[x].son[xpos^1];
        tr[tr[x].son[xpos^1]].fa=y;
        tr[x].son[xpos^1]=y;
        pushup(y);return ;
    }
    void splay(int x){
        int now=x;pth[++pt]=now;
        while(nroot(now))pth[++pt]=now=tr[now].fa;
        while(pt)pushdown(pth[pt--]);
        while(nroot(x)){
            int y=tr[x].fa,z=tr[y].fa;
            int xpos=get(x),ypos=get(y);
            if(nroot(y)){
                if(xpos==ypos)rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
        pushup(x);return ;
    }
    void access(int x){
        for(int y=0;x;y=x,x=tr[x].fa)
            splay(x),tr[x].son[1]=y,pushup(x);
    }
    void makeroot(int x){
        access(x);splay(x);pushr(x);
    }
    int findroot(int x){
        access(x);splay(x);
        while(tr[x].son[0])pushdown(x),x=tr[x].son[0];
        splay(x);return x;
    }
    void split(int x,int y){
        makeroot(x);access(y);splay(y);
    }
    bool link(int x,int y){
        makeroot(x);
        if(findroot(y)==x)return false;
        tr[x].fa=y;return true;
    }
    bool cut(int x,int y){
        makeroot(x);
        if(findroot(y)!=x||tr[y].fa!=x||tr[y].son[0])return false;
        tr[y].fa=tr[x].son[1]=0;pushup(x);return true;
    }
    bool lt(int x,int y){
        makeroot(x);
        if(findroot(y)==x)return true;
        else return false;
    }
}lct;
int n,m,ans=0x3f3f3f3f;
struct E{int x,y,a,b;}e[N];
bool com(E a,E b){return a.a<b.a;}
signed main(){
    n=read();m=read();
    fo(i,1,m)e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();
    sort(e+1,e+m+1,com);
    fo(i,1,m){
        // cout<<e[i].a<<" "<<e[i].b<<endl;
        if(lct.lt(e[i].x,e[i].y)){
            lct.split(e[i].x,e[i].y);
            if(lct.tr[e[i].y].mx<=e[i].b)continue;
            lct.tr[i+n].val=e[i].b;lct.pushup(i+n);
            int id=lct.tr[e[i].y].id-n;
            lct.cut(e[id].x,id+n);
            lct.cut(e[id].y,id+n);
            lct.link(e[i].x,i+n);
            lct.link(e[i].y,i+n);
        }
        else {
            lct.tr[i+n].val=e[i].b;lct.pushup(i+n);
            lct.link(e[i].x,i+n);
            lct.link(e[i].y,i+n);
        }
        if(lct.lt(1,n))lct.split(1,n),ans=min(ans,lct.tr[n].mx+e[i].a);
    }
    printf("%d",ans==0x3f3f3f3f?-1:ans);
}

Luogu3703 SDOI2017 树点涂色

庆祝一下在\(JBB\)的指导之下,通过了此题!!!

刚开始看这个题的时候,不可做!!

数种类数的题最难了,不知道从哪里下手

然而可以发现一个性质,相同颜色的都在一起,就是连续的

所以说这个颜色种类数满足可减性,我们第二问就解决了,直接求出来\(LCA\)减一下就行了

那么我们看第一问,我们只关心颜色种类数

我们发现,我们更改的路径会对路径两侧的子树造成加一的贡献

而对路径上的数,会造成减的贡献

于是不知道咋想的,题解就想到了\(LCT\),这个确实很像吧

虚边表示颜色变化,实边表示相同的颜色,也就是说我们每一颗\(splay\)维护的都是一个相同颜色的连通块

那么我们再用一颗线段树维护每一个节点到根节点的颜色种类数

因为满足可减性,那就直接减

最大值,直接查子树最大值就行了

修改就是打通\(x\)到根的路径,\(access\)时更新线段树答案

断掉的实边表示增加答案,连上的实边表示减少答案

code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*t;
}
const int N=1e5+5;
int n,m;
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
    to[++rp]=y;
    nxt[rp]=head[x];
    head[x]=rp;
}
int siz[N],son[N],dep[N],fa[N];
int top[N],dfn[N],dfm[N],cnt,idf[N];
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    int mx[N*4],tag[N*4];
    void pushup(int x){mx[x]=max(mx[ls],mx[rs]);}
    void pushdown(int x){
        mx[ls]+=tag[x];
        tag[ls]+=tag[x];
        mx[rs]+=tag[x];
        tag[rs]+=tag[x];
        tag[x]=0;
    }
    void ins(int x,int l,int r,int ql,int qr,int v){
        if(ql>qr)return ;
        if(ql<=l&&r<=qr){
            mx[x]+=v;tag[x]+=v;
            return ;
        }
        if(tag[x])pushdown(x);
        int mid=l+r>>1;
        if(ql<=mid)ins(ls,l,mid,ql,qr,v);
        if(qr>mid)ins(rs,mid+1,r,ql,qr,v);
        pushup(x);
    }
    int query(int x,int l,int r,int ql,int qr){
        if(ql>qr)return 0;
        if(ql<=l&&r<=qr)return mx[x];
        if(tag[x])pushdown(x);
        int mid=l+r>>1,ret=0;
        if(ql<=mid)ret=max(ret,query(ls,l,mid,ql,qr));
        if(qr>mid)ret=max(ret,query(rs,mid+1,r,ql,qr));
        pushup(x);return ret;
    }
    #undef ls
    #undef rs
}xds;
struct LCT{
    struct POT{
        int son[2],fa;
        bool rev;
    }tr[N];
    bool nroot(int x){return tr[tr[x].fa].son[0]==x||tr[tr[x].fa].son[1]==x;}
    int get(int x){return tr[tr[x].fa].son[1]==x;}
    void rotate(int x){
        int y=tr[x].fa,z=tr[y].fa;
        int xpos=get(x),ypos=get(y);
        if(nroot(y))tr[z].son[ypos]=x;
        tr[x].fa=z;tr[y].fa=x;
        tr[y].son[xpos]=tr[x].son[xpos^1];
        tr[tr[x].son[xpos^1]].fa=y;
        tr[x].son[xpos^1]=y;
    }
    void splay(int x){
        while(nroot(x)){
            int y=tr[x].fa,z=tr[y].fa;
            int xpos=get(x),ypos=get(y);
            if(nroot(y)){
                if(xpos==ypos)rotate(y);
                else rotate(x);
            }
            rotate(x);
        }
    }
    int findrt(int x){
        while(tr[x].son[0])x=tr[x].son[0];
        return x;
    }
    void access(int x){
        for(int y=0;x;y=x,x=tr[x].fa){
            splay(x);
            if(tr[x].son[1]){
                int now=findrt(tr[x].son[1]);
                xds.ins(1,1,n,dfn[now],dfm[now],1);
            }
            if(y){
                int now=findrt(y);
                xds.ins(1,1,n,dfn[now],dfm[now],-1);
            }
            tr[x].son[1]=y;
        }
    }
}lct;
void dfs_fi(int x,int f){
    fa[x]=f;dep[x]=dep[f]+1;
    lct.tr[x].fa=f;
    siz[x]=1;son[x]=0;
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==fa[x])continue;
        dfs_fi(y,x);
        siz[x]+=siz[y];
        if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
    }
}
void dfs_se(int x,int f){
    top[x]=f;dfn[x]=++cnt;idf[cnt]=x;
    
    if(son[x])dfs_se(son[x],f);
    for(int i=head[x];i;i=nxt[i]){
        int y=to[i];
        if(y==son[x]||y==fa[x])continue;
        dfs_se(y,y);
    }
    dfm[x]=cnt;
    xds.ins(1,1,n,dfn[x],dfm[x],1);
}
int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
signed main(){
    n=read();m=read();
    fo(i,1,n-1){
        int x=read(),y=read();
        add_edg(x,y);add_edg(y,x);
    }
    dfs_fi(1,0);dfs_se(1,1);
    fo(i,1,m){
        int tp=read(),x=read(),y;
        if(tp==1)lct.access(x);
        if(tp==2){
            y=read();int lca=LCA(x,y);
            printf("%d\n",xds.query(1,1,n,dfn[x],dfn[x])+xds.query(1,1,n,dfn[y],dfn[y])-2*xds.query(1,1,n,dfn[lca],dfn[lca])+1);
        }
        if(tp==3){
            printf("%d\n",xds.query(1,1,n,dfn[x],dfm[x]));
        }
    }
}
posted @ 2021-12-14 20:15  fengwu2005  阅读(86)  评论(0编辑  收藏  举报