LCT(link cut tree)

维护一个包含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])]);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/dogeding/article/details/82288575
今日推荐