第一阶段集训(这篇先写写tarjan以及圆方树)

  第一阶段的集训结束了w,不得不说oi太长时间不整是会退步的。

  怎么说好呢,集训这几天过的很充实,知识收货很多,题调的也不少,自己的目标更明确了吧,不过这几天集训也是可以看出蒟蒻就是蒟蒻,还是太菜了。。。。不过会努力的w。

  首先得感谢一下兔哥,假期还来给我们上课,还要饱受牙疼的折磨。。。再次感谢兔哥,%%%%%大神就是大神w。

  结束之后想想这两天都记住什么了w,第一天讲了tarjan,印象最深的还是圆方树这个知识点吧,毕竟这个是个木有学习过的知识。

算了,要不我还是顺道复习一下强联通分量blabla什么的吧。。。。。。。

  + 强联通分量(不要告诉我博客园不支持markdown)。。。。

  在有向图G中,如果两个顶点u,v之间存在一条路径u到v的路径而且也存在一条v到u的路径,则称这两个顶点u,v是强联通的。如果有向图G的每两个顶点都强联通,称G是一个强联通图。有向非强联通图的极大强联通子图,称之为强联通分量。若将有向图中的强联通分量都缩成一个点,则原图会形成一个DAG。

  + 那么我们一般求强联通分量的方法是tarjan,那么它是怎么实现的w?

  其实,tarjan本质上是个dfs,dfs过程中向栈中push当前节点。当找到一个强联通分量的时候,(其包含的节点一定都在栈中连续排列),so我们把它们全部都pop掉,这些元素构成一个强联通分量。

  每个节点u有两个值——dfn[u]和low[u]。dfn[u]就是dfs序中的位置,low[u]就是u经过最多一条栈中横叉边所能到达的dfn最小的点。这里我们解释一下什么是横叉边,因为我们tarjan是dfs,这棵dfs树上有原图上的边,在dfs树上由父亲指向儿子的叫做树枝边,由父亲指向儿子下面其他后代的叫做前向边,由后代指向祖先的叫做后向边,像其他的边(一个子树中的点指向另一个子树中的点)称之为横叉边。栈中横叉边,顾名思义,就是终点是栈里面元素的边。

  那么问题来了,如何求我们这个所谓的“经过最多一条栈中横叉边能到达的dfn最小的点的dfn”(也就是low)?;

  当我们dfs到的当前点为点u,枚举u能到达的点v。若v没被dfs过,递归dfs点v,并且用low[v]更新low[u](就是low[u] = min(low[u],low[v])),如果v被dfs过且仍在栈中,说明u—>v这条边是一条栈中横叉边,用dfn[v]更新low[u]。

  如果所有v枚举完以后,low[u]仍等于dfn[u],则此时栈中u及u以上的节点同属于一个强联通分量,要一起弹出。

  此处应该有图片。。。。。我也不知道它能不能看到,应该可吧

  然后我们说一说tarjan的性质:

    + 各个强联通分量出栈的顺序是一定的——就是缩点形成的DAG的拓扑序的逆序。

    拓扑序就是DAG中的一种排序,要求任一条边的终点排在起点之后。

    so,tarjan可以求拓扑序,好写???

  tarjan的代码的话。

void tarjan(int u){
    dfn[u] = low[u] = ++idx;
    stk[+top] = u,ins[u] = 1;
    for(int i = adj[u],v;i;i = nxt[i]){
        if(!dfn[v = go[i]]){
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(ins[v]){
            low[u] = min(low[u],low[v]);
        }
    }
    if(low[u] == dfn[u]){
        ncnt++;
        do{
            bel[stk[top]] = ncnt;
            ins[stk[top]] = 0;
        }while(stk[top--] != u);
    }
}

  

  例题讲了挺多w,像联通数(暴力bfs加02爆锤标程),以及迷失在公园里找不到方向(逛公园)

  我记得还讲了无向图的tarjan来着????

  这个好像需要引入几个概念???

    + 割点:将这个点删去后图不连通;

    + 割边: 讲这条边删去后图不连通;

    + 点双联通分量:无割点的极大联通子图。

    + 边双联通分量:无割边的极大联通子图。

  然后如何用tarjan求割边,边双?

    在无向图的dfs树中,原图中的边不可能是横叉边,只可能是前(后)向边或树枝边。所以无向图的tarjan相比有向图很好写,无需讨论某条边是否为栈中横叉边。

    一条无向边(u,v)是割边(dfn[u] < dfn[v])当且仅当它是树枝边,而且low[v] > dfn[u](这时子树v中所有点不能到达子树之外)

    这时我们发现,删去原图中所有割边,剩下的连通块都是边双。

  然后还有tarjan求割点qww

    当u不是dfs树的根的时候,对于树枝边(u,v)(u为v的父亲),如果low[v] >= dfn[u],就是子树v中的点都不通往到子树u之外,那么删去点u,子树v中的点与子树u以外的点不了联通,u为割点。

    但是呢,当u是dfs的根的时候,判断low[v] >= dfn[u]是不够的。因为对于任意的v都满足这个条件啊w,(当子树u以外没有点,上面判断割点的理由就不成立了w)。

    so,我们怎么判断呢?如果u有至少两个子树,则u是割点,只有一个子树,u不是割点。

    还有要值得注意的是,删去所有割点,剩下的联通块并不是点双qww。(这个还挺显然的吧)。

    那么我们怎么求点双呢???

    其中一种求点双的方法是把边push进栈中,当发现low[v] >= dfn[u]时,把边(u,v)及它以上的所有边从栈中弹出,这些边所涉及的点共同形成一个点双(显然包括u)。

    另一种方法是像别的tarjan算法一样把点压入栈中,当发现low[v]  >= dfn[u]时,把v及v以上的点从栈中弹出,再加上u,共同形成一个点双。代码的话。。。。。

    tarjan求割点:

void tarjan(int u,int pre){
    int v,child_cnt = 0;
    dfn[u] = low[u] ++index;
    for(int i = head[u];i;i = nxt[i]){
        if(!dfn[v = to[i]]){
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v] >= dfn[u]){
                child_cnt++;
                iscut[u] = 1;
            }
        else if(v != pre){
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(u == 1 && child_cnt == 1)
        iscut[u] = 0;
}

    求割边,边双代码:

void tarjan(int u,int pre){
    stk[++top] = u;
    dfn[u] = low[u] = ++idx;
    for(int i = head[u],v;i;i = nxt[i]){
        if(!dfn[v = to[i]]){
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v] > dfn[u]){
                is_bridge[i] = is_bridge[i ^ 1] = 1;
                cnt++;
                do
                    bel[stk[top]] = cnt;
                while(stk[top--] != v);
            }
        }
         else if(v != pre){
                low[u] = min(low[u],dfn[u]);
         }
    }
}

  下面终于进入了我们正题了,圆方树!!!!!!!!!!!

  what is it?

    一个无向联通图可以对应一棵圆方树(广义的)。

    圆方树中有圆点和方点。

    圆点是原图中存在的点,方点对应原图的点双。原图中跨点双的边予以保留,点双内部的边删掉,同时所有方点与其对应点双中的圆点连边。

    此处应该有图片:

    

   那么我们怎么建一棵圆方树呢?

    我们新建一个链前,按照定义对每个点双建一个方点,然后把它和点双中的所有点连边,再把原图中跨点双的边也连上。

    显然,可以一边tarjan求点双,一边把边连了。  

在这里举一道棵题:

  给出一张图,点有点权,每次询问两点之间的简单路径中,权值的最小值最小是多少。n,m,q <=1000000.

  这个题我们把圆方树建出,方点权值为包含圆点权值最小值,然后这个问题就转化成了求链上最小值。可倍增,可树剖。。。。over

原文地址:https://www.cnblogs.com/excellent-zzy/p/11257081.html