上一篇讲了如何应用Tarjan算法求出e-DCC和v-DCC。

那么这一篇就是e-DCC和v-DCC的应用之一:缩点。

先讲e-DCC的缩点。

我们把每一个e-DCC都看成一个节点,把所有桥边(x,y)看成连接编号为c[x]和c[y]的两个e-DCC间的边,这样我们就会得到一棵树或者森林(原图不连通)。给出缩点的代码,这份代码把e-DCC缩点并把生成的树(森林)储存在另一个邻接表中。

  1. #include<bits/stdc++.h>
  2. #define N 100010
  3. using namespace std;
  4. inline int read(){
  5. int data=,w=;char ch=;
  6. while(ch!='-' && (ch<''||ch>''))ch=getchar();
  7. if(ch=='-')w=-,ch=getchar();
  8. while(ch>='' && ch<='')data=data*+ch-'',ch=getchar();
  9. return data*w;
  10. }
  11. struct Edge{
  12. int nxt,to;
  13. #define nxt(x) e[x].nxt
  14. #define to(x) e[x].to
  15. }e[N<<];
  16. struct EdgeC{
  17. int nxtc,toc;
  18. #define nxtc(x) ec[x].nxtc
  19. #define toc(x) ec[x].toc
  20. }ec[N<<];
  21. int head[N],tot=,n,m,cnt,dfn[N],low[N],c[N],bridge[N],dcc;
  22. int headc[N],totc=;
  23. inline void addedge(int f,int t){
  24. nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
  25. }
  26. inline void addedge_c(int f,int t){
  27. nxtc(++totc)=headc[f];toc(totc)=t;headc[f]=totc;
  28. }
  29. void tarjan(int x,int in_edge){
  30. dfn[x]=low[x]=++cnt;
  31. for(int i=head[x];i;i=nxt(i)){
  32. int y=to(i);
  33. if(!dfn[y]){
  34. tarjan(y,i);
  35. low[x]=min(low[x],low[y]);
  36. if(low[y]>dfn[x])
  37. bridge[i]=bridge[i^]=;
  38. }else if(i!=(in_edge^))
  39. low[x]=min(low[x],dfn[y]);
  40. }
  41. }
  42. void dfs(int x){
  43. c[x]=dcc;
  44. for(int i=head[x];i;i=nxt(i)){
  45. int y=to(i);
  46. if(c[y]||bridge[i])continue;
  47. dfs(y);
  48. }
  49. }
  50. int main(){
  51. n=read();m=read();
  52. for(int i=;i<=m;i++){
  53. int x=read(),y=read();
  54. addedge(x,y);addedge(y,x);
  55. }
  56. for(int i=;i<=n;i++)
  57. if(!dfn[i])tarjan(i,);
  58. for(int i=;i<=n;i++){
  59. if(!c[i]){
  60. ++dcc;dfs(i);
  61. }
  62. }
  63. for(int i=;i<=tot;i++){
  64. int x=to(i^),y=to(i);
  65. if(c[x]==c[y])continue;
  66. addedge_c(c[x],c[y]);
  67. }
  68. //缩点后的树(森林)的点数为dcc,边数为totc/2
  69. for(int i=;i<totc;i++)
  70. printf("%d %d",toc(i^),toc(i));
  71. return ;
  72. }

v-DCC的缩点由于一个割点可能在很多个v-DCC中而更加麻烦,但是我们也有办法缩。

假设图中有x个割点和y个v-DCC,我们就直接建(x+y)个点的新图。

每一个v-DCC和割点都作为新图的节点存在。建完后我们让每个割点和包含它的v-DCC连边。

给出代码:

  1. #include<bits/stdc++.h>
  2. #define N 100010
  3. using namespace std;
  4. inline int read(){
  5. int data=,w=;char ch=;
  6. while(ch!='-' && (ch<''||ch>''))ch=getchar();
  7. if(ch=='-')w=-,ch=getchar();
  8. while(ch>='' && ch<='')data=data*+ch-'',ch=getchar();
  9. return data*w;
  10. }
  11. struct Edge{
  12. int nxt,to;
  13. #define nxt(x) e[x].nxt
  14. #define to(x) e[x].to
  15. }e[N<<];
  16. struct EdgeC{
  17. int nxtc,toc;
  18. #define nxtc(x) ec[x].nxtc
  19. #define toc(x) ec[x].toc
  20. }ec[N<<];
  21. int head[N],tot=,n,m,rt,dfn[N],low[N],cnt,stk[N],top,num,cut[N];
  22. int headc[N],totc=,new_id[N];
  23. vector<int> dcc[N];
  24. inline void addedge(int f,int t){
  25. nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
  26. }
  27. inline void addedge_c(int f,int t){
  28. nxtc(++totc)=headc[f];toc(totc)=t;headc[f]=totc;
  29. }
  30. void tarjan(int x){
  31. dfn[x]=low[x]=++cnt;
  32. stk[++top]=x;
  33. if(x==rt && head[x]==){
  34. dcc[++num].push_back(x);
  35. return;
  36. }
  37. int flag=;
  38. for(int i=head[x];i;i=nxt(i)){
  39. int y=to(i);
  40. if(!dfn[y]){
  41. tarjan(y);
  42. low[x]=min(low[x],low[y]);
  43. if(low[y]>=dfn[x]){
  44. flag++;
  45. if(x!=rt||flag>)cut[x]=;
  46. num++;int z;
  47. do{
  48. z=stk[top--];
  49. dcc[num].push_back(z);
  50. }while(z!=y);
  51. dcc[num].push_back(x);
  52. }
  53. }else low[x]=min(low[x],dfn[y]);
  54. }
  55. }
  56. int main(){
  57. n=read();m=read();
  58. for(int i=;i<=m;i++){
  59. int x=read(),y=read();
  60. addedge(x,y);addedge(y,x);
  61. }
  62. for(int i=;i<=n;i++)
  63. if(!dfn[i])tarjan(i);
  64. cnt=num;//给每个割点一个新的编号防止重复,从num+1开始
  65. for(int i=;i<=n;i++)
  66. if(cut[i])new_id[i]=++cnt;
  67. for(int i=;i<=cnt;i++){
  68. for(int j=;j<dcc[i].size();j++){
  69. int x=dcc[i][j];
  70. if(cut[x]){//割点和每个v-DCC连边
  71. addedge_c(i,new_id[x]);
  72. addedge_c(new_id[x],i);
  73. }else new_id[x]=i;
  74. }
  75. }
  76. //缩点后的森林(树)点数为cnt,边数为totc/2
  77. for(int i=;i<totc;i+=)
  78. printf("%d %d\n",toc(i^),toc(i));
  79. return ;
  80. }

下一篇更新Tarjan求有向图的SCC以及SCC的缩点

[Tarjan系列] 无向图e-DCC和v-DCC的缩点的更多相关文章

  1. [Tarjan系列] Tarjan算法求无向图的桥和割点

    RobertTarjan真的是一个传说级的大人物. 他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决. 而且,Tarjan并不只发明了LCT,他对 ...

  2. Tarjan求无向图割点、桥详解

    tarjan算法--求无向图的割点和桥   一.基本概念 1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥无向连通图中,如果删除某边后,图变成不 ...

  3. 牛客D-Where are you /// kruskal+tarjan找无向图内的环

    题目大意: https://ac.nowcoder.com/acm/contest/272/D 在一个无向图中,给定一个起点,从起点开始走遍图中所有点 每条边有边权wi,表示第一次经过该道路时的花费( ...

  4. 『Tarjan算法 无向图的双联通分量』

    无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...

  5. [Tarjan系列] Tarjan算法求无向图的双连通分量

    这篇介绍如何用Tarjan算法求Double Connected Component,即双连通分量. 双联通分量包括点双连通分量v-DCC和边连通分量e-DCC. 若一张无向连通图不存在割点,则称它为 ...

  6. Tarjan系列1

    tajan的dfs树系列算法: 求解割点,桥,强连通分量,点双联通分量,边双联通分量: tajan是一个dfs,把一个图变成一个dfs树结构, dfs树结构,本质是通过一个没有任何要求的dfs把图的边 ...

  7. Tarjan系列算法总结(hdu 1827,4612,4587,4005)

    tarjan一直是我看了头大的问题,省选之前还是得好好系统的学习一下.我按照不同的算法在hdu上选题练习了一下,至少还是有了初步的认识.tarjan嘛,就是维护一个dfsnum[]和一个low[],在 ...

  8. 『Tarjan算法 无向图的割点与割边』

    无向图的割点与割边 定义:给定无相连通图\(G=(V,E)\) 若对于\(x \in V\),从图中删去节点\(x\)以及所有与\(x\)关联的边后,\(G\)分裂为两个或以上不连通的子图,则称\(x ...

  9. tarjan系列算法代码小结

    个人使用,可能不是很详细 强联通分量 这里的dfn可以写成low 因为都是在栈中,只要保证该节点的low值不为本身即可 void tarjan(int now) { dfn[now]=low[now] ...

随机推荐

  1. 数据库groub by分组后,把多行数据合并成一行数据(Oracle、Postgres)

    关键字 row_number() over (partition by)   例如,下面的数据, 这是按照name分组后,展示property值. 我们想得到这样的值; 第一步:将每一组的proper ...

  2. MyBatis入门使用

    MyBatis入门使用 MyBatis简介 MyBatis是支持普通SQL查询.存储过程和高级映射的持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.MyBati ...

  3. pwn学习日记Day17 《程序员的自我修养》读书笔记

    静态链接章小结 本章首先学习了静态链接的第一步骤,即目标文件在被链接成最终可执行文件时,输入目标文件中的各段是如何被合并到输出文件中的,链接器如何为它们分配在输出文件中的空间和地址.一旦输入段中的最终 ...

  4. JMeter压力测试及并发量计算-1

    一.JMeter的安装(Linux) 1. 下载JMeter:这个就不细说了,直接去(http://jmeter.apache.org/download_jmeter.cgi)下载. 2. 解压:ta ...

  5. Linux设备驱动程序 之 内核定时器

    综述 如果需要在将来的某个时间点调度执行某个动作,同时在该时间点到达之前不会阻塞当前进程,则可以使用内核定时器: 内核定时器是一个数据结构,它告诉内核在用户定义的时间点使用用户定义的参数来执行一个用户 ...

  6. Jenkins 搭建企业实战案例 (发布与回滚)

    让我们的代码部署变得easy,不再难,Jenkins是一个可扩展的持续集成引擎,是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能.Jenkins非常易于安装和配置,简单易用 ...

  7. 记录一次vxworks下使用NFS组件的过程

    问题:有三块CPU都运行vxworks6.9,现在想要CPU3做server,CPU1-2通过NFS访问CPU3上的文件 补充:使用防火墙可能会影响NFS访问,目前我还没有找到解决办法... 下面是过 ...

  8. laravel5.1设置cookie

    Laravel 所建立的 cookie 会加密并且加上认证记号,这代表着被用户擅自更改的 cookie 会失效.从请求中取得Cookie值,你使用cookie方法 $value = $request- ...

  9. Sql 中常用日期转换Convert(Datetime) convert datetime

    Convert(data_type,expression[,style]) Convert(varchar(10),字段名,转换格式) 说明:此样式一般在时间类型(datetime,smalldate ...

  10. swift 第八课 CollectView的 添加 footerView 、headerView

    collectView 也是 iOS 很常用的瀑布流展示控件了,虽然使用过很多次,一直没有系统的总结过,尤其是在添加header 和footer view 的时候,很常见,写起来总觉得不是很流畅,这里 ...