LCT能干啥

 
 
 

模板:

 

  • 维护可加的树链信息:询问都是一条链上的信息;维护方式和线段树差不多;
  • 增加一条边;
  • 删除一条边;
  • 修改一个点权;
  • 修改一条路径上的所有点的点权;

 
 

整体来说,像是树链剖分的森林化,再用\(splay\)代替线段树;

 
 

\(eg.\) 染色

Tree II

 
 


 
 

维护\(MST\)

 
 

\(eg.\) 水管局长

 
 

题目要求删边,感觉删边比加边麻烦,我们可以用时光倒流法,将操作离线,沿时间轴从后往前倒着操作(撤回),会更加方便;

 
 

因为每往\(MST\)(最小生成树)中加入一条边,会产生一个环,需要在这个环上删除一条最大的边才能,重新得到\(MST\),这是\(LCT\)可以做到的;

 
 

我们遇到的的第一个问题是边权化点权,一个很方便的方法是,将每一条边都看成一个点,比如对于边\((x,y)\),它的编号是\(i\),每次连接时,都将\((x,i)\)\((i,y)\)相连,这样构造出来的联通性是等价的;

边的权值被存储在\(i\)节点中,巧妙的解决了边权的问题;

 
 

对于维护\(MST\),可以记录这颗子树里最大的边权的编号,每次增加一条边,再将路径上最大的边删去即可;

 
 

 


 
 

维护双连通分量??

 
 

\(eg.\) 航线规划

感觉题目跟边双联通分量有关,将边双缩了点后,两点之间的边的数量就是我们要求的;

 
 

那删除一条边呢?

经验告诉我们,要时光倒流;

那加入一条边呢?

要重新缩点,那意思是······动态缩点???在\(LCT\)上缩点???

 
 

还是从题目本身入手吧;

先把边权化点权,用前面的方法,一个由边变来的点,才拥有权值,如果值为\(1\),意味着这条边是关键边,反之不是;

\(LCT\)上就是维护一个子树和,询问查询路径和;

 
 

那加入一条边呢??(又来了)

比如加一条\((x,y)\),那\(x\)\(y\)就会产生一个环,那\(x\)\(y\)之间的边全都不再是关键边;那就可以打标记推平;

 
 

至此,这个题就是路径推平路径查询;

 
 
 


 
 

维护子树信息

 
 

链的情况我们能解决,那子树呢?

首先一定要明确,原树上的子树和\(LCT\)上的子树有巨大差别;

\(eg.\) 大融合

发现询问就是,在原树上删除\((x,y)\)得到的子树大小的乘积;

 
 

根据题目询问的特点,每次询问一条边\((x,y)\),我们可以把\(x,y\)这条链先单独取出来\(split(x,y)\)

这样我们所需要的信息就是\(x,y\)的虚子树大小的和;

 
 

怎么维护虚树子树信息(记为\(sz[x]\))???

我们发现虚树边只有在\(access\)\(link\)函数时会发生改变;

 
 

\(access\)原函数

void access(int x)
{ 
    for(int y=0;x;y=x,x=fa[x])
        splay(x),rs=y,upd(x);
}

\(x\)的右儿子变了,我们应当在\(sz[x]\)减去\(y\)的贡献,而加上\(rs\)的贡献;

贡献是什么?这里应该指的是,实儿子和虚儿子总的子树大小和,毕竟\(x\)虚儿子所在的整个\(splay\)树都在\(x\)子树中;

这一部分应该为

void upd(int x){ s[x]=s[ls]+s[rs]+1+sz[x];}
void access(int x)
    {
        for(int y=0;x;x=fa[y=x])
        {
            splay(x),
            sz[x]+=s[rs];
            sz[x]-=s[y];
            rs=y,upd(x);
        }
    }

 
 

\(link\)呢?

原函数

void link(int x,int y)
{
    makert(x);fa[x]=y;
}

这里就是连了一条虚边;

那应该把\(x\)的贡献加到\(y\)里;

但由于\(y\)的位置未知,就必须还把\(x\)的贡献加给\(y\)的父亲们;

 
 

为了解决这个问题,我们可以让\(y\)没有父亲;

具体来说把\(y\) \(splay\)到根的位置就可以了,即

void link(int x,int y)
{
    makert(x);access(y);splay(y);
    fa[x]=y;
    sz[y]+=s[x];
    upd(y);
}

 
 

这样就可以正确维护虚子树信息。

\(Code\)

#include<bits/stdc++.h>
#define ll long long 
#define mp make_pair
using namespace std;

const int N=200005;
int n,m;

inline int read()
{
    int x=0,fl=1;char st=getchar();
    while(st<'0'||st>'9'){ if(st=='-')fl=-1; st=getchar();}
    while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
    return x*fl;
}

struct LCT
{
    #define ls ch[x][0]
    #define rs ch[x][1]
    int fa[N],ch[N][2],tag[N],sz[N];
    int v[N],s[N];
    int sta[N];
    bool nort(int x){ return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
    void upd(int x){ s[x]=s[ls]+s[rs]+1+sz[x];}
    void rev(int x){ swap(ls,rs);tag[x]^=1;}
    void pd(int x)
    {
        if(tag[x])
        {
            if(ls) rev(ls);
            if(rs) rev(rs);
            tag[x]=0;
        }
    }
    void rotate(int x)
    {
        int y=fa[x],ys=(ch[y][1]==x);
        int R=fa[y];
        int B=ch[x][ys^1];
        if(nort(y)) ch[R][ch[R][1]==y]=x; ch[x][ys^1]=y; ch[y][ys]=B;
        if(B) fa[B]=y; fa[x]=R; fa[y]=x;
        upd(y);upd(x);
    }
    void splay(int x)
    {
        int y=x,z,top=0;
        sta[++top]=y;
        while(nort(y))
        {
            y=fa[y];
            sta[++top]=y;
        }
        while(top) pd(sta[top--]);
        while(nort(x))
        {
            y=fa[x];z=fa[y];
            if(nort(y))
                rotate((ch[y][1]==x)==(ch[z][1]==y)?y:x);
            rotate(x);
        }
    }
    void access(int x)
    {
        for(int y=0;x;x=fa[y=x])
        {
            splay(x),
            sz[x]+=s[rs];
            sz[x]-=s[y];
            rs=y,upd(x);
        }
    }
    void makert(int x)
    {
        access(x);splay(x);rev(x);
    }
    void split(int x,int y)
    {
        makert(x);access(y);splay(y);
    }
    void link(int x,int y)
    {
        makert(x);access(y);splay(y);
        fa[x]=y;
        sz[y]+=s[x];
        upd(y);
    }
}f;

map<pair<int,int>,int> book;
char st[5];

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++) f.s[i]=1;
    for(int i=1;i<=m;i++)
    {
        scanf("%s",st);
        int x=read(),y=read();
        if(st[0]=='A')
        {
            f.link(x,y);
        }
        else
        {
            f.split(x,y);
            printf("%lld\n",(ll)(f.sz[x]+1)*(f.sz[y]+1));
        }
    }
    return 0;
}

 
 
 
 

\(to\ be\ continued\)

版权声明:本文为yudes原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/yudes/p/LCT.html