芝士: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
连边(link)
先\(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;
}

浙公网安备 33010602011771号