LCT的全称是Link-Cut-Tree(林克卡特树(滑稽))
听起来好像是个很高大上的数据结构,其实……还好吧,这里一个一个操作分析
首先,要学LCT必须先要有Splay的基础,这里安利一篇blog
LCT主要就是维护树的动态更改(动态树,要求维护动态的森林,解决动态森林上的问题)。
说白了就是可以维护树上路径问题(动态的)
这题就是LCT的模板题,就是可以在原树上随便断边和连边,更改路径上的点的点权,然后维护一条路径上的最小点权。
先不要着急离开,其实LCT的核心就不到20行,代码长(其实还好)是因为有颗Splay。
首先,为了解决树上路径问题,先要把树划分成树链。
不要想树链剖分(虽然有点像),但是LCT是XJB划分的。
为蛤呢?
因为树是动态的……
也就是划分是动态的(disgusting),不过不怕。。
所以说LCT动态划分要求用可以维护树链动态变化,从任意位置断开,连接等奇奇怪怪的操作,还要能迅速维护树上的信息。
所以最合适的数据结构是:Splay
是不是很有道理(滑稽
首先是树链的划分:
这就是树链的划分,注意,它是根据需要划分的(就是说可以乱搞)
然后每颗Splay维护一条树链(就是按照深度维护),然后图上的虚边用Path_Parent来表示,就相当于fa。。
最核心的Access操作,Access操作的目的就是把x连向原树的根,并且x是这条树链的结尾,就是在这条链上深度最深的,说白了就是把x的儿子也断开,看看代码就明白了(最好可以画个图)
代码:
void access(int x){
int y=0;
while(x){
Splay(x);//首先还是要把x转到根(注意是维护这个序列的Splay的根)
if(ch[x][1]){//然后看右儿子有没有连东西,有就断开,就是把原树结点x一下的在Splay上断开
fa[ch[x][1]]=0;
Path_Parent[ch[x][1]]=x;//把Path_Parent连向结点x,就是原树的虚边
}
ch[x][1]=y;//连向新的儿子
if(y) fa[y]=x;//嗯……
Path_Parent[y]=0;//因为联通了,所以可以不用连Path_Parent
update(x);//记得更新
y=x;//继续向上跑
x=Path_Parent[x];//跑
}
}
好了,这个就是其他很多操作的基础。
下面继续讲其他操作
寻根:找到点x的根(在原树上的)
int getroot(int x){
access(x);//首先要把x和根连通
Splay(x);//因为不知道Splay的根,所以要先把x旋转到根(方便处理)
for(;ch[x][0];x=ch[x][0],pushdown(x)); //因为是按深度排序,所以根肯定是在Splay上最小的(深度最小)
return x;//返回(原树的根)
}
evert:讲x设为原树的根
void evert(int x){
access(x); //保证和根联通
Splay(x);//旋转到根
rev[x]^=1;//打翻转标记,因为把x设为根就相当于把x到根的这个序列整个翻转
}
link:将两个点连在一起(原树)
void link(int x,int y){
evert(y);//让其中一个点作为树根
Splay(y);//然后旋转到根
Path_Parent[y]=x;//最后……就这样了
}
cut:断边(原树)
void cut(int x,int y){
evert(x);//将x作为原树树根
access(y);//把y和树根连通(因为x和y在同一棵树,所以就相当于把x和y连通)
Splay(y);//转到根
if(ch[y][0]){//把y的左儿子断开(在Splay上的),因为这是按照深度维护的Splay,所以x的深度肯定比y的小,所以断开y的左子树。
fa[ch[y][0]]=0;
ch[y][0]=0;
}
update(x);//记得更新
}
ad:把一条路径上的所有的点权+z
void ad(int x,int y,int z){
evert(x);//还是把x作为树根(原树)
access(y);//依然是连通y y->x
Splay(y);//旋转到根
add[y]+=z;//然后把这条路径打上标记
}
query:最后询问(套路也一样)
int query(int x,int y){//同上
evert(x);
access(y);
Splay(y);
return ma[y];
}
没了!!!!
这些就是基本操作,是不是很短,下面贴上完整代码:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define mem(x) memset(x,0,sizeof(x))
using namespace std;
const int N = 300005;
int val[N],ma[N],ch[N][2],fa[N],Path_Parent[N],add[N],rev[N];
void init(){//清空用
mem(val);
mem(ma);
mem(ch);
mem(Path_Parent);
mem(add);
mem(rev);
mem(fa);
}
void pushdown(int x){//下放标记
if(rev[x]){
swap(ch[x][0],ch[x][1]);
if(ch[x][0]) rev[ch[x][0]]^=1;
if(ch[x][1]) rev[ch[x][1]]^=1;
rev[x]=0;
}
if(add[x]){
ma[x]+=add[x];
val[x]+=add[x];
if(ch[x][0]) add[ch[x][0]]+=add[x];
if(ch[x][1]) add[ch[x][1]]+=add[x];
add[x]=0;
}
}
void update(int x){//上传标记
if(!x) return;
ma[x]=val[x];
pushdown(ch[x][0]);//我打的是延时标记,所以在上传的时候要先下放
pushdown(ch[x][1]);
ma[x]=max(ma[x],max(ma[ch[x][0]],ma[ch[x][1]]));
}
int get(int x){
return ch[fa[x]][1]==x;
}
void rotate(int x){//Splay的旋转
int f=fa[x],gf=fa[f],k=get(x);
if(!f) return;
if(gf) ch[gf][get(f)]=x;
fa[x]=gf;
fa[ch[x][!k]]=f;
ch[f][k]=ch[x][!k];
ch[x][!k]=f;
fa[f]=x;
Path_Parent[x]=Path_Parent[f];//Path_Parent只用维护在Splay的根就可以了
Path_Parent[f]=0;
update(f);
update(x);
}
int sta[N];
void Splay(int x){
int now=x,top=0;
sta[++top]=now;
while(fa[now]) now=fa[now],sta[++top]=now;
while(top) pushdown(sta[top--]); //记得要先下放标记
for(int f;f=fa[x];rotate(x)) if(get(f)==get(x)&&fa[f]) rotate(f);
update(x);
}
void access(int x){//以下部分上面已讲
int y=0;
while(x){
Splay(x);
if(ch[x][1]){
fa[ch[x][1]]=0;
Path_Parent[ch[x][1]]=x;
}
ch[x][1]=y;
if(y) fa[y]=x;
Path_Parent[y]=0;
update(x);
y=x;
x=Path_Parent[x];
}
}
int getroot(int x){
access(x);
Splay(x);
for(;ch[x][0];x=ch[x][0],pushdown(x));
return x;
}
void evert(int x){
access(x);
Splay(x);
rev[x]^=1;
}
void link(int x,int y){
evert(y);
Splay(y);
Path_Parent[y]=x;
}
void cut(int x,int y){
evert(x);
access(y);
Splay(y);
if(ch[y][0]){
fa[ch[y][0]]=0;
ch[y][0]=0;
}
update(x);
}
void ad(int x,int y,int z){
evert(x);
access(y);
Splay(y);
add[y]+=z;
}
int query(int x,int y){
evert(x);
access(y);
Splay(y);
return ma[y];
}
int n,u[N],v[N],t;
int main(){
while(~scanf("%d",&n)){
init();
for(int i=1;i<n;i++) scanf("%d%d",&u[i],&v[i]);
for(int i=1;i<=n;i++) scanf("%d",&val[i]),ma[i]=val[i];
for(int i=1;i<n;i++) link(u[i],v[i]);
scanf("%d",&t);
while(t--){
int c,x,y,z;
scanf("%d",&c);
if(c==1){
scanf("%d%d",&x,&y);
if(getroot(x)==getroot(y)) {printf("-1\n");continue;}
link(x,y);
}
else
if(c==2){
scanf("%d%d",&x,&y);
if(x==y||getroot(x)!=getroot(y)) {printf("-1\n");continue;}
cut(x,y);
}
else
if(c==3){
scanf("%d%d%d",&z,&x,&y);
if(getroot(x)!=getroot(y)) {printf("-1\n");continue;}
ad(x,y,z);
}
else
if(c==4){
scanf("%d%d",&x,&y);
if(getroot(x)!=getroot(y)) {printf("-1\n");continue;}
printf("%d\n",query(x,y));
}
}
printf("\n");
}
return 0;
}
还好吧……