这个题使我更深理解了TARJAN算法,题意:无向图,每添加一条边后文桥的数量,三种解法:(按时间顺序),1,暴力,每每求桥,听说这样能过,我没过,用的hash判重,这次有俩个参数(n->10w,开不了二维的),怎么判?联系2个参数,我想到了用一个函数,像散列一样,定义关系,我随便写了一个hash[x+y+x/y+y/x+x%y+y%x+x|y],一直WA,虽然未过,但是想到了这个,以后2个参数判重可以用之!2.网上学习了算法,将之缩点成树,每个双连通分量用一个点表示,用一个数组tree[i],点i属于tree[i]值,然后记录下桥,重新用一个FATHER【i】来建树,具体见代码。3.学习了算法后,其实不用缩点!直接搞起!因为tarjan算法本生成树!用后面一个点来标记边即可啊!(开始总部知道怎么标记!)并更加理解了树枝边和返祖边,桥是树枝边,无向图的所有边分为树枝边和返祖边(后向边),都是实际存在的。直接在原图上找LCA(按DFN值来判断)即可。

//(原创于2014.2.18)今复习之用,重新理解后,重新编辑方法3。

在hduj交爆栈,前加一句话:即可AC

  1. #pragma comment(linker, "/STACK:10240000000000,10240000000000")// 申请空间

  1. #include<iostream> //暴力,WA
  2. #include<vector>
  3. #include<cstring>
  4. #include<cstdio>
  5. using namespace std;
  6. vector<vector<int> >edge(100001);
  7. int dfn[100001];
  8. int low[100001];
  9. int visited[100001]; //标记访问
  10. int times=0; //时间戳
  11. int hash[210001];
  12. int num_bridge=0;
  13. int min(int a,int b)
  14. {
  15. if(a<=b)return a;
  16. return b;
  17. }
  18. void tarjan(int u,int fa) //dfs
  19. {
  20. dfn[u]=low[u]=++times;
  21. int daxiao=edge[u].size();
  22. for(int i=0;i<daxiao;i++)
  23. {
  24. int child=edge[u][i];
  25. if(visited[child]==0)
  26. {
  27. visited[child]=1;
  28. tarjan(child,u);
  29. low[u]=min(low[u],low[child]);
  30. if(dfn[u]<low[child]&&hash[u+child+2*u%child+2*child%u+3*u/child+3*child/u]<=1) //是桥
  31. {
  32. num_bridge++;
  33. }
  34. }
  35. else if(edge[u][i]!=fa)
  36. {
  37. low[u]=min(dfn[edge[u][i]],low[u]);
  38. }
  39. }
  40. }
  41. int main()
  42. {
  43. int n,m;
  44. int tcase=1;
  45. while (~scanf("%d%d",&n,&m)&&(n||m))
  46. {
  47. for(int i=0;i<=n;i++)
  48. {
  49. edge[i].clear();
  50. }
  51. for(int i=0;i<210001;i++)
  52. {
  53. hash[i]=0;
  54. }
  55. int a,b;
  56. for(int i=0;i<m;i++)
  57. {
  58. scanf("%d%d",&a,&b);
  59. edge[a].push_back(b);
  60. edge[b].push_back(a);
  61. hash[a+b+2*a%b+2*b%a+3*a/b+3*b/a]++;
  62. }
  63. int que;scanf("%d",&que);
  64. printf("Case %d:\n",tcase);
  65. tcase++;
  66. while(que--)
  67. {
  68. times=0;
  69. num_bridge=0;
  70. for(int i=0;i<=n;i++)
  71. {
  72. low[i]=dfn[i]=visited[i]=0;
  73. }
  74. scanf("%d%d",&a,&b);
  75. edge[a].push_back(b);
  76. edge[b].push_back(a);
  77. hash[a+b+2*a%b+2*b%a+3*a/b+3*b/a]++;
  78. visited[1]=1;
  79. tarjan(1,-1);
  80. printf("%d\n",num_bridge);
  81. }
  82. printf("\n");
  83. }
  84. return 0;
  85. }
  1. #include<iostream> //方法2,poj 2000MS AC,HOJ RE(stack over)
  2. #include<vector>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<stack>
  6. #include<queue>
  7. using namespace std;
  8. int dfn[100001]; //
  9. int low[100001];
  10. int visited[100001]; //tarjan标记访问
  11. int father[100001]; //缩点后建成一棵树
  12. int head[100001];
  13. bool instack[100001];
  14. int tree[100001]; //每个边双连通分量中的点属于一个集合,函数值1-block
  15. int level[100001];
  16. bool mark[100001]; //dfs标记
  17. bool is_bridge[100001]; //标记桥,形成树后,用点来标记边也可,
  18. stack<int>s;
  19. vector<vector<int> >bridge(100001);
  20. int min(int a,int b)
  21. {
  22. if(a<=b)return a;
  23. return b;
  24. }
  25. struct edges //边
  26. {
  27. int pre,to;
  28. };
  29. struct bridges //桥
  30. {
  31. int from,to;
  32. };
  33. int times=0; int num_bridge=0; int block; //时间戳,桥数量,“块”数(边双连通分量数)
  34. vector<edges>edge(400001); vector<bridges> ve;
  35. void tarjan(int u,int fa) //走一遍,求出桥
  36. {
  37. dfn[u]=low[u]=++times;
  38. instack[u]=1;
  39. s.push(u); //入栈,
  40. for(int i=head[u];i!=-1;i=edge[i].pre)
  41. {
  42. int child=edge[i].to;
  43. if(visited[child]==0)
  44. {
  45. visited[child]=1;
  46. tarjan(child,u);
  47. low[u]=min(low[u],low[child]);
  48. if(dfn[u]<low[child]) //是桥,保存起来
  49. {
  50. num_bridge++;
  51. bridges temp;
  52. temp.from=u;temp.to=child;
  53. ve.push_back(temp);
  54. }
  55. }
  56. else if(child!=fa)
  57. {
  58. low[u]=min(dfn[child],low[u]);
  59. }
  60. }
  61. if(dfn[u]==low[u]) //发现一个边双连通分量 blosk++
  62. {
  63. block++;
  64. int now=s.top();
  65. while(now!=u)
  66. {
  67. instack[now]=0;
  68. s.pop();
  69. tree[now]=block;
  70. now=s.top();
  71. }
  72. instack[now]=0;
  73. s.pop();
  74. tree[now]=block;
  75. }
  76. }
  77. void dfs(int u,int lev) //走一遍DFS,自制缩成一棵树,用father【i】来连接,规定了方向,并记录每个点深度。
  78. {
  79. level[u]=lev;
  80. int len=bridge[u].size();
  81. for(int i=0;i<len;i++)
  82. {
  83. int v=bridge[u][i];
  84. if(mark[v]==0)
  85. {
  86. father[v]=u;
  87. mark[v]=1;
  88. dfs(v,lev+1);
  89. }
  90. }
  91. }
  92. void lca(int u,int v) //每次询问添加的边,调用一次LCA,将路经上(按father和深度level向上走)的标记为非桥。
  93. {
  94. if(level[u]>level[v]){int temp=v;v=u;u=temp;}
  95. while(level[v]>level[u])
  96. {
  97. if(is_bridge[v])
  98. {
  99. num_bridge--;
  100. is_bridge[v]=0;
  101. }
  102. v=father[v];
  103. }
  104. while(v!=u)
  105. {
  106. if(is_bridge[v])
  107. {
  108. num_bridge--;
  109. is_bridge[v]=0;
  110. }
  111. if(is_bridge[u])
  112. {
  113. num_bridge--;
  114. is_bridge[u]=0;
  115. }
  116. v=father[v];
  117. u=father[u];
  118. }
  119. }
  120. int main()
  121. {
  122. int n,m;
  123. int tcase=1;
  124. while (~scanf("%d%d",&n,&m)&&(n||m))
  125. {
  126. for(int i=0;i<100001;i++)
  127. {
  128. level[i]=mark[i]=tree[i]=father[i]=low[i]=dfn[i]=visited[i]=0;
  129. head[i]=-1;
  130. bridge[i].clear();
  131. is_bridge[i]=1;
  132. }
  133. ve.clear();
  134. int a,b;
  135. num_bridge=block=times=0;
  136. for(int i=0;i<2*m;i++) //读入
  137. {
  138. scanf("%d%d",&a,&b);
  139. edge[i].to=b;
  140. edge[i].pre=head[a];
  141. head[a]=i;
  142. i++;
  143. edge[i].to=a;
  144. edge[i].pre=head[b];
  145. head[b]=i;
  146. }
  147. visited[1]=1;
  148.  
  149. tarjan(1,-1);
  150.  
  151. for(int i=0;i<ve.size();i++)
  152. {
  153. bridge[tree[ve[i].from]].push_back(tree[ve[i].to]);
  154. bridge[tree[ve[i].to]].push_back(tree[ve[i].from]);
  155. }
  156. mark[1]=1;
  157. dfs(1,0);
  158. int que;scanf("%d",&que);
  159. printf("Case %d:\n",tcase);
  160. tcase++;
  161. while(que--)
  162. {
  163. scanf("%d%d",&a,&b);
  164. lca(tree[a],tree[b]);
  165. printf ("%d\n",num_bridge);
  166. }
  167. printf("\n");
  168. }
  169. return 0;
  170. }
  1.  

方法3:

  1. #pragma comment(linker, "/STACK:10240000000000,10240000000000")// 申请空间,否则爆。Hdu 460MS ac
  2. #include<iostream> //poj 1000MS AC,
  3. #include<vector>
  4. #include<cstring>
  5. #include<cstdio>
  6. using namespace std;
  7. int dfn[100001];
  8. int low[100001];
  9. bool visited[100001]; //tarjan标记访问
  10. int father[100001]; //缩点后建成一棵树
  11. int head[100001]; //每个边双连通分量中的点属于一个集合,函数值1-block
  12. bool is_bridge[100001]; //标记桥,形成树后,用点来标记边也可,
  13. int min(int a,int b)
  14. {
  15. if(a<=b)return a;
  16. return b;
  17. }
  18. struct edges //前向星保存边
  19. {
  20. int pre,to;
  21. };
  22. int times=0; int num_bridge=0; //时间戳,桥数量,
  23. vector<edges>edge(400001);
  24. void tarjan(int u,int fa) //走一遍,求出桥,不忘了。dfs生成的是树!所以可以标记后面一个点来标记边!无向图tarjan俩个变量
  25. {
  26. dfn[u]=low[u]=++times;
  27. for(int i=head[u];i!=-1;i=edge[i].pre)
  28. {
  29. int child=edge[i].to;
  30. if(visited[child]==0) //理解这里是树枝边(向前)!
  31. {
  32. visited[child]=1;
  33. father[child]=u; //生成树的父亲
  34. tarjan(child,u);
  35. low[u]=min(low[u],low[child]);
  36. if(dfn[u]<low[child]) //是桥,保存起来,标记后一个点即可(生成树每个点对应一条到它的边)
  37. {
  38. num_bridge++;
  39. is_bridge[child]=1;
  40. }
  41. }
  42. else if(child!=fa) //返祖边(向后边)点已经访问,说明child是u的某个祖先!
  43. {
  44. low[u]=min(dfn[child],low[u]);
  45. }
  46. }
  47. }
  48. void lca(int u,int v) //每次询问添加的边,调用一次LCA,将路经上(按father和dfn向上走)的标记为非桥。dfn恰好是树的的层次。
  49. {
  50. if(dfn[u]>dfn[v]){int temp=v;v=u;u=temp;}
  51. while(dfn[v]>dfn[u]) //到同一层为止。
  52. {
  53. if(is_bridge[v])
  54. {
  55. num_bridge--;
  56. is_bridge[v]=0;
  57. }
  58. v=father[v];
  59. }
  60. while(v!=u) //到公共祖先为止。
  61. {
  62. if(is_bridge[v])
  63. {
  64. num_bridge--;
  65. is_bridge[v]=0;
  66. }
  67. if(is_bridge[u])
  68. {
  69. num_bridge--;
  70. is_bridge[u]=0;
  71. }
  72. v=father[v];
  73. u=father[u];
  74. }
  75. }
  76. int main()
  77. {
  78. int n,m;
  79. int tcase=1;
  80. while (~scanf("%d%d",&n,&m)&&(n||m))
  81. {
  82. for(int i=0;i<100001;i++)
  83. {
  84. dfn[i]=father[i]=low[i]=dfn[i]=visited[i]=0;
  85. head[i]=-1;
  86. is_bridge[i]=0;
  87. }
  88. int a,b;
  89. num_bridge=times=0;
  90. for(int i=0;i<2*m;i++) //读入
  91. {
  92. scanf("%d%d",&a,&b);
  93. edge[i].to=b;
  94. edge[i].pre=head[a];
  95. head[a]=i;
  96. i++;
  97. edge[i].to=a;
  98. edge[i].pre=head[b];
  99. head[b]=i;
  100. }
  101. visited[1]=1;
  102. tarjan(1,-1);
  103. int que;scanf("%d",&que);
  104. printf("Case %d:\n",tcase);
  105. tcase++;
  106. while(que--)
  107. {
  108. scanf("%d%d",&a,&b);
  109. lca(a,b);
  110. printf ("%d\n",num_bridge);
  111. }
  112. printf("\n");
  113. }
  114. return 0;
  115. }

poj3694+hdu2460 求桥+缩点+LCA/tarjan的更多相关文章

  1. HDU-4612 Warm up,tarjan求桥缩点再求树的直径!注意重边

    Warm up 虽然网上题解这么多,感觉写下来并不是跟别人竞争访问量的,而是证明自己从前努力过,以后回头复习参考! 题意:n个点由m条无向边连接,求加一条边后桥的最少数量. 思路:如标题,tarjan ...

  2. 无向连通图求割边+缩点+LCA

    Network Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 7082   Accepted: 2555 Descripti ...

  3. hdoj 4612 Warm up【双连通分量求桥&&缩点建新图求树的直径】

    Warm up Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Su ...

  4. POJ3694 Network 边双缩点+LCA+并查集

    辣鸡错误:把dfs和ldfs搞混...QAQ 题意:给定一个无向图,然后查询q次,求每次查询就在图上增加一条边,求剩余割边的个数. 先把边双缩点,然后预处理出LCA的倍增数组: 然后加边时,从u往上跳 ...

  5. POJ 3694 Network(无向图求桥+重边处理+LCA)

    题目大意: 给你一个无向图,然后再给你一个Q代表有Q次询问,每一次加一条边之后还有几座桥.在这里要对重边进行处理. 每次加入一条边之后,在这条搜索树上两个点的公共祖先都上所有点的桥都没了. 这里重边的 ...

  6. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)

    Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...

  7. tarjan算法(强连通分量 + 强连通分量缩点 + 桥(割边) + 割点 + LCA)

    这篇文章是从网络上总结各方经验 以及 自己找的一些例题的算法模板,主要是用于自己的日后的模板总结以后防失忆常看看的, 写的也是自己能看懂即可. tarjan算法的功能很强大, 可以用来求解强连通分量, ...

  8. 【并查集缩点+tarjan无向图求桥】Where are you @牛客练习赛32 D

    目录 [并查集缩点+tarjan无向图求桥]Where are you @牛客练习赛32 D PROBLEM SOLUTION CODE [并查集缩点+tarjan无向图求桥]Where are yo ...

  9. POJ 3694 (tarjan缩点+LCA+并查集)

    好久没写过这么长的代码了,题解东哥讲了那么多,并查集优化还是很厉害的,赶快做做前几天碰到的相似的题. #include <iostream> #include <algorithm& ...

随机推荐

  1. 实现流水灯以间隔500ms的时间闪烁(系统定时器SysTick实现的精确延时)

    /** ****************************************************************************** * @file main.c * ...

  2. AWS Data Lake Service Stack

  3. (转)编码剖析Spring管理Bean的原理

    http://blog.csdn.net/yerenyuan_pku/article/details/52832434 在Spring的第一个案例中,我们已经知道了怎么将bean交给Spring容器进 ...

  4. (转)Spring的三种实例化Bean的方式

    http://blog.csdn.net/yerenyuan_pku/article/details/52832793 Spring提供了三种实例化Bean的方式. 使用类构造器实例化. <be ...

  5. 【整理】iview中刷新页面的时候更新导航菜单的active-name

    iview中刷新页面的时候更新导航菜单的active-name https://blog.csdn.net/lhjuejiang/article/details/83212070

  6. faster rcnn训练过程讲解

    http://blog.csdn.net/u014696921/article/details/60321425

  7. android滚动图片

    关于广告轮播,大家肯定不会陌生,它在现手机市场各大APP出现的频率极高,它的优点在于"不占屏",可以仅用小小的固定空位来展示几个甚至几十个广告条,而且动态效果很好,具有很好的用户& ...

  8. C# string补位

    参考:https://www.cnblogs.com/zhangqs008/archive/2012/02/01/2341078.html //1.1.左补位 "; , 'A'); //1. ...

  9. 牛客noip前集训营(第一场)提高T1

    链接:https://www.nowcoder.com/acm/contest/172/A来源:牛客网 题目描述 小N得到了一个非常神奇的序列A.这个序列长度为N,下标从1开始.A的一个子区间对应一个 ...

  10. perl学习之:use & require

    相同: 都可以用来引用module(.PM). 不同: 1) 区别在于USE是在当前默认的@INC里面去寻找,一旦模块不在@INC中的话,用USE是不可以引入的,但是require可以指定路径: 2) ...