LCT模板及详细讲解

Link cut tree

从一个例子引入:

bzoj1036

此题有两种解法,这里只提LCT的方法。

一棵树上有n个节点,有3个操作:

1、将节点u的权值改为t。

2、询问从点u到点v的路径上的节点的最大权值

3、询问从点u到点v的路径上的节点的权值和

如果学过树链剖分的话此题是可以轻易地解决的,可引入LCT解此题。

LCT换句话说就是多个splay树链来链去,连起来再断开。

其中最基础的操作-----Access,也是最不好理解的。

如果我讲的不能理解的话可以参考杨哲论文《QTREE 解法的一些研究》

Access

如果节点v的子树中,最后被访问的节点在子树w中,则称w为v的Preferred Child,v到w这条边就是Preferred Edge,而由Preferred Edge 连成的路径为Preferred Path,所以,整棵树被划分成了若干条Preferred Path。

如果调用了Access(v),则v的父节点的Preferred Child就变成了v,这样应该可以很好理解Access操作了吧,如果不行可以看论文里的例子。

真实代码和伪代码有点不同。

void access(int x){
    for(int t=0;x;t=x,x=fa[x])
        splay(x),tr[x][1]=t,update(x);
}


接下来介绍其他一些操作:

Makeroot(x):使x所在的splay树中把x作为根节点。

将x的父亲节点的Preferred Child 改为x,然后splay操作将x转到树根,同时rev[x]取反。

void makeroot(int x){
    access(x);splay(x);rev[x]^=1;
}

Link(x,y):将x节点与y节点连边。

将x节点变为根节点,然后让fa[x]=y;

void link(int x,int y){
    makeroot(x);fa[x]=y;
}

Split(x,y):将x到y的路径取出(分离)。

void split(int x,int y){
    makeroot(x);access(y);splay(y);
}


求解u到v的路径上的节点权值和只需split(u,v),然后输出sum[v]即可。


整体代码:


#include 
#include 
#include 
#include 
#include 
#include 
#define N 30000+30
#define ll long long
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,top,fa[N],tr[N][2],u[N],v[N],q[N];
ll w[N],sum[N],mx[N];
bool rev[N];
bool isroot(int x){
    return tr[fa[x]][0]!=x && tr[fa[x]][1]!=x;
}
void update(int x){
    int l=tr[x][0],r=tr[x][1];
    sum[x]=sum[l]+sum[r]+w[x];
    mx[x]=max(w[x],max(mx[l],mx[r]));
}
void pushdown(int x){
    int l=tr[x][0],r=tr[x][1];
    if(rev[x]){
        rev[x]^=1;rev[l]^=1;rev[r]^=1;
        swap(tr[x][0],tr[x][1]);
    }
}
inline void rotate(int x){
    int f=fa[x],ff=fa[f],l,r;
    if(tr[f][0]==x) l=0;else l=1;r=l^1;
    if(!isroot(f)) tr[ff][tr[ff][1]==f]=x;
    fa[tr[x][r]]=f;fa[f]=x;fa[x]=ff;
    tr[f][l]=tr[x][r];tr[x][r]=f;
    update(f);update(x);
}
void splay(int x){
    q[++top]=x;
    for(int i=x;!isroot(i);i=fa[i]) q[++top]=fa[i];
    while(top) pushdown(q[top--]);
    while(!isroot(x)){
        int f=fa[x],ff=fa[f];
        if(!isroot(f)){
            if(tr[f][0]==x ^ tr[ff][0]==f) rotate(x);
            else rotate(f);
        }
        rotate(x);
    }
}
void access(int x){
    for(int t=0;x;t=x,x=fa[x])
        splay(x),tr[x][1]=t,update(x);
}
void makeroot(int x){
    access(x);
    splay(x);
    rev[x]^=1;
}
void link(int x,int y){
    makeroot(x);fa[x]=y;
}
void split(int x,int y){
    makeroot(x);
    access(y);
    splay(y);
}
int main()
{
    n=read();mx[0]=-(1e9);
    for(int i=1;i
比树链剖分慢qwq

这只是个开始,后面还有很多LCT的变形,这是裸的模板题。

你可能感兴趣的:(BZOJ,LCT,OI)