维护一个包含N个点的森林, 并且支持形态和权值信息的操作。
以上一句话大概可以描述LCT的用途。
再看一些不知道有多大用处的概念。
一个结点可能“偏好(prefer)”某个儿子,使它成为“优先儿子(preferred child)”。
优先儿子与它父亲之间的边称为“优先边(preferred edge)”。
仅由优先边组成的路径称为“优先路径(preferred path)”。优先路径有可能只有一个结点。
话不多说,直接扯实现吧OVO
可以这么思考:将一棵树剖成很多链,由于是动态的,所以就用splay来维护。
这里先把splay的操作放上来*(主要是尽量防止各位dalao看不懂我写的是什么,而且这里的Splay操作和之前用的不太一样)*:
struct node *NIL;
struct node
{
int re,i,siz;
node *fa,*ch[2];
int Dir() { return this==fa->ch[1]; }
bool Isroot() { return fa==NIL||(this!=fa->ch[0]&&this!=fa->ch[1]); }
void Setchild(node *x,int d)
{
ch[d]=x;
if(x!=NIL) x->fa=this;
}
void Pushup() { siz=ch[0]->siz+ch[1]->siz+1; }
void Pushdown()
{
if(re)
{
swap(ch[0],ch[1]);
if(ch[0]!=NIL) ch[0]->re^=1;
if(ch[1]!=NIL) ch[1]->re^=1;
re=0;
}
}
}tree[maxt],*ncnt,*np[maxt];//这里的np就是每个点的地址
void Init()
{
ncnt=NIL=&tree[0];
NIL->fa=NIL->ch[0]=NIL->ch[1]=NIL;
NIL->siz=NIL->re=0;
}
node *Newnode(int i)
{
ncnt++;
ncnt->i=i,ncnt->siz=1,ncnt->re=0;
ncnt->fa=ncnt->ch[0]=ncnt->ch[1]=NIL;
return ncnt;
}
void Rotate(node *x)
{
node *y=x->fa;
y->Pushdown(); x->Pushdown();
int d=x->Dir();
if(y->Isroot()) x->fa=y->fa;
else y->fa->Setchild(x,y->Dir());
y->Setchild(x->ch[!d],d);
x->Setchild(y,!d);
y->Pushup();
}
void Splay(node *x)
{
x->Pushdown();
while(!x->Isroot())
{
node *y=x->fa;
if(y->Isroot()) Rotate(x);
else
{
if(x->Dir()==y->Dir()) Rotate(y);
else Rotate(x);
Rotate(x);
}
}
x->Pushup();
}
接下来就讲LCT的操作了。
Access:将x到根节点的路径设为优先路径。
也就是说,每次将x伸展到根,然后右儿子连到某个y,再令y=x,x=x->fa。
void Access(node *x)
{
node *y=NIL;
while(x!=NIL)
{
Splay(x);
x->Setchild(y,1);
x->Pushup();
y=x; x=x->fa;
}
}
Makeroot:换根,将x所在splay逆序。(为什么要逆序我不是很懂…QAQ)
void Makeroot(node *x)
{
Access(x);
Splay(x);
x->re^=1;
}
Link:添加边。就是将x所在的树和y所在的树合并。
void Link(node *x,node *y)
{
Makeroot(x);
x->fa=y;
Splay(x);
}
Cut:删除边。就是把x和y的父子关系完全断掉。
因为Access(y)时y在x右边,所以Splay之后x就在y的左子树上了。
void Cut(node *x,node *y)
{
Makeroot(x);
Access(y);
Splay(y);
y->ch[0]=NIL;
y->Pushup();
x->fa=NIL;
}
说了一通实现,那么有什么用呢?
求LCA。
之前了解了倍增求LCA,现在了解一下用LCT求LCA吧*(emmm…这一段好像也不是很懂的样子)*
int Find(node *x,node *y)
{
Access(x); Splay(x);
Splay(y);
node *rt=y->fa;
while(rt!=NIL)
{
y=rt;
Splay(y);
rt=y->fa;
}
return y->i;
}
来道板题:BZOJ 2002 HNOI2010弹飞绵羊
int main()
{
Init();
scanf("%d",&n);
for(int i=0;i<=n;i++) np[i]=Newnode(i);
for(int i=0;i<n;i++)
{
scanf("%d",&k[i]);
Link(np[i],np[min(n,i+k[i])]);
}
scanf("%d",&q);
while(q--)
{
int tp,x,y;
scanf("%d",&tp);
if(tp==1)
{
scanf("%d",&x);
Makeroot(np[n]);
Access(np[x]); Splay(np[x]);
printf("%d\n",np[x]->ch[0]->siz);
}
else
{
scanf("%d%d",&x,&y);
Cut(np[x],np[min(n,x+k[x])]);
k[x]=y;
Link(np[x],np[min(n,x+k[x])]);
}
}
}