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;
}
将x节点变为根节点,然后让fa[x]=y;
void link(int x,int y){
makeroot(x);fa[x]=y;
}
void split(int x,int y){
makeroot(x);access(y);splay(y);
}
整体代码:
#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的变形,这是裸的模板题。