zoukankan      html  css  js  c++  java
  • 【动态树】动态树的应用 (总)

    【动态树】动态树的应用 (总)

    一、维护链的信息

    通过split在树中处理出一条链,y点所储存的值就是答案

    当要处理树的边权相关的问题时,我们可以化一条边为一个带权的点,就可以一般化处理了

    二、动态维护连通性&强联通分量

    维护连通性:findroot进行判断,其他就是link、cut的操作了

    维护强联通分量:如果要连结A和B,若发现A和B在同一颗LCT中,则dfs缩点,就是把形成的环的fa指针指向A或B,但要注意在access时要改为

    1 void access (int x)
    2 {
    3     for (int y=0;x;y=x,x=fa[y]=find (fa[x]))
    4         splay (x),ch[x][1]=y,update (x);
    5 }

    维护强联通分量只支持加边,同时我们可以把动态删边离线倒序成动态加边

    三、维护边权(常用于维护生成树)

    利用LCT我们就可以动态维护一颗生成树

    例如维护最小生成树:在一棵树中加入一条边时,会产生一个环,我们只需删去换上权值最大的一条边,在加入这条边即可

    四、维护虚子树信息总和与原树信息总和

    如果题目要我们维护一颗树的总size,我们就要引入虚子树;

    原来我们update这样写:

    1 void update (int x)
    2 {
    3     size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
    4 }

    现在我们新增一个数组si [u],用来维护u虚边所连的子树的size总和,显然一颗树的总size为si [u]+size[u]

    所以我们把update改为:

    1 void update (int x)
    2 {
    3     size[x]=size[ch[x][0]]+size[ch[x][1]]+si[x]+1;
    4

    然后我们怎么维护si [MAXN]数组呢?

    在link,access我们做出以下修改

    1 void access (int x)
    2 {
    3     for (int y=0;x;x=fa[y=x])
    4         splay (x),si[x]+=size[ch[x][1]],si[x]-=size[ch[x][1]=y],update (x);
    5 }
    6 void link (int x,int y)
    7 {
    8     makeroot (x),access (y),splay (y),si[fa[x]=y]+=size[x],update (y);
    9 }

    在access时,会有虚边和实边的转化,我们进行删除和增加即可

    同样link我们也如此操作,但我们要先将y变为没有fa的点,不然y的size与si改了,但它的祖先没有更改

    五、维护树上染色联通块

    我们可以对于每个联通快都开一个LCT,但如果数据是菊花图,肯定会被卡爆

    因为树的形态是不会改变的,所以我们选定一个根,将无根树转化为有根树

    这里以黑白为例:

    我们在开两个LCT,一个存白色的联通块,一个存黑色联通块;

    因为对于每个点,只有一条连父亲的边(根结点不连)

    每个点有两个状态:
    1、该点为白色,在白LCT中该点link其父亲结点

    2、该点为黑色,在黑LCT中该点link其父亲结点

    很显然,每个结点的颜色发生修改时我们只需在两棵LCT中修改对应父亲结点的边即可

    所以时间复杂度可以保证O(QlogN)

    图中通过父子连边形成了若干个联通块,我们达成了第一步

    因为是父子连边,我们要先dfs一遍

    1 void dfs (int u,int f)
    2 {
    3     for (int i=head[u];i!=0;i=e[i].nxt)
    4         if (e[i].v!=f)
    5         {
    6             LCT[col[1]].link (e[i].v,u),fa[e[i].v]=u;
    7             dfs (e[i].v,u);
    8         }
    9 }

    我们在link,cut要修改一下

     1 void link (int x,int y)
     2 {
     3     if (!y) return;
     4     access (y),splay (x),splay (y);
     5     fa[x]=y,si[y]+=size[x],update (y);
     6 }
     7 void cut (int x,int y)
     8 {
     9     if (!y) return;
    10     access (x),splay (x);
    11     ch[x][0]=fa[ch[x][0]]=0,update (x);
    12 }

    因为树根是固定的,所以切记不能使用makeroot,在splay时也不需pushdown

     1 void splay (int x)
     2 {
     3     while (nroot (x))
     4     {
     5         int y=fa[x];
     6         if (nroot (y)) rotate (get (x)==get (y)?y:x);
     7         rotate (x);
     8     }
     9     update (x);
    10 }

    其中update一定要加,因为如果该树中只有x一个结点,便不会rotate,size[x]和si[x]便不会改变,仍为0,导致WA

    或者在初始定义时 for (int i=1;i<=n;i++) size[i]=1;定义一遍

    操作代码:

    要特判根结点的颜色是否与当前结点相同,否则除去根结点的答案

     1 while (m--)
     2 {
     3     int opt=read (),x=read ();
     4     if (opt==1)
     5         LCT[col[x]].cut (x,fa[x]),col[x]^=1,LCT[col[x]].link (x,fa[x]);
     6     else
     7     {
     8         LCT[col[x]].access (x);int rt=LCT[col[x]].findroot (x);
     9         if (col[rt]==col[x]) printf ("%d
    ",LCT[col[x]].size[rt]);
    10         else printf ("%d
    ",LCT[col[x]].size[LCT[col[x]].ch[rt][1]]);
    11     }
    12 }
  • 相关阅读:
    12. nc/netcat 用法举例
    7. 由一道ctf学习变量覆盖漏洞
    11. 几点基于Web日志的Webshell检测思路
    约瑟夫环
    栈结构的经典算法题
    二叉查找树之二
    fork与vfork
    数组常见算法题
    赛马问题
    fibonacci 数列及其应用
  • 原文地址:https://www.cnblogs.com/PaulShi/p/10063984.html
Copyright © 2011-2022 走看看