其实就是v-DCC点双的缩点。
之所以有这个玩意主要是无向图的V—DCC缩点非常恶心,尤其是用到割点时一个割点可能会在多个v-DCC里建图时只放在一个里就会悲伤的WA;
考试考到我才学(........)
李煜东上讲的挺好的,但还是有点理解不透,所以写一下。
上面是基于求出割点后的,然后我发现假如没有割点,新图就成0个点不就凉了吗?这种情况........答案是0呗,做道题就知道了。
完全建的新图。。。
在求割点时求出了每个割点内部的点和割点数量。为建图做准备。
不严谨的说圆方树,就是这张新图,对于每个割点我们都建立一个方点然后把割点及内部一堆(v-DCC)缩了当成一个圆点,所以一个圆点一定连一个方点,一个方点一定连一个圆点。
考虑如何避免把割点少放入圆点中的情况。我们把割点放入每个连通块(每个连通块都形成了一个圆点)。这就是它优秀的地方之一。
理解一下李煜东上的代码(只放关键部分)
{
1.割点中判了孤立点: if (x==root&&!head[x]) dcc[++cnt].push_back(x); 因为这样它没有跑tarjan但他确实是个割点(有个性);
2.if(x!=root||flag>1) 因为如果是根节点的话有两棵子树及以上才会是割点。
3.求割点中没有像直接缩点一样用到mark;
4.关键部分建图上代码(东哥上的这里只做解释)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
num=cnt;//从cnt也就是已有的v_dcc数量开始编号因为这是新建的点而v-dcc已经有了自己的编号 for(int i=1;i<=n;i++) if(cut[i]) new_id[i]=++num;对割点建一个方点(可以yy成一个虚有的节点充当了一个节); 注意这里是找的i对于一个i节点它在搜的时候会在每一个连通块中刷存在,所以这样建新点保证一个只会建一个而不重复。 tc=1; for(int i=1;i<=cnt;i++) for(int j=0;j<dcc[i].size();j++) { int x=dcc[i][j];//枚举内部点 if(cut[x]) add(i,new_id[x]); add(new_id[x],i);这里有点难理解可以看到add的是i和new_id[x],new_id是方点啊!这就是上面说的每个割点不只出现一次而要在每个连通块里都有,所以对于割点找其方点连上这样我们就可以把割点连上而且随时访问,又做到了无形中缩点可以说是非常优秀了。 else c[x]=i; 对于不是割点的点我们只要知道它在那个块了就行,其实就是缩点中的belong 用不用的就看题了。 }
}
补充做题心得:1.用for循环清空会比memset快很多尤其是多测数据大的时候,但一定一定要注意new_id及新建图中的head新Dfs中的judge,mark,都可能会下标大于N,所以加上for(int i=n;i<=cnum;++i) head[i]=judge[i]=mark[i]=0;
2.其实在求割点的时候根据题目要求加上一些强大的细节特判,(细节巨多码量精简),就只求割点就行了,但圆方树把用到的割点提取了出来从而少了一些思考和判断,思路清晰。各有优点。
3.一般来说把信息保留在方点中最后每个割点访问其方点就可以获得信息。
4.dfs时要从bl[1]开始但bl[1]可能为零因为1可能是割点这时就看对代码灵活度的考察了,在If(cut[v])中我们把bl[v]设成new_id[v],到时候就可以愉快的dfs这里是把1当做了根去搜,从1的方点开始与1开始效果相同。而有无影响及bl[割点]设成什么一般就看题目并注意细节即可。