Problem Description
City C is really a nightmare of all drivers for its traffic jams. To solve the traffic problem, the mayor plans to build a RTQS (Real Time Query System) to monitor all traffic situations. City C is made up of N crossings and M roads, and each road connects two crossings. All roads are bidirectional. One of the important tasks of RTQS is to answer some queries about route-choice problem. Specifically, the task is to find the crossings which a driver MUST pass when he is driving from one given road to another given road.
 
Input
There are multiple test cases.
For each test case:
The first line contains two integers N and M, representing the number of the crossings and roads.
The next M lines describe the roads. In those M lines, the ith line (i starts from 1)contains two integers Xi and Yi, representing that roadi connects crossing Xi and Yi (Xi≠Yi).
The following line contains a single integer Q, representing the number of RTQs.
Then Q lines follows, each describing a RTQ by two integers S and T(S≠T) meaning that a driver is now driving on the roads and he wants to reach roadt . It will be always at least one way from roads to roadt.
The input ends with a line of “0 0”.
Please note that: 0<N<=10000, 0<M<=100000, 0<Q<=10000, 0<Xi,Yi<=N, 0<S,T<=M 
 
Output
For each RTQ prints a line containing a single integer representing the number of crossings which the driver MUST pass.
 
题目大意:给一个N个点M条边的无向图,然后有Q个询问X,Y,问第X条边到第Y条边必需要经过的点有多少个。
思路:对于两条边X,Y,若他们在同一个双连通分量中,那他们之间至少有两条路径可以互相到达。
那么,对于两条不在同一个分量中的边X,Y,显然从X到Y必须要经过的的点的数目等于从X所在的边双连通分量到Y所在的双连通分量中的割点的数目。
于是,可以找出所有双连通分量,缩成一个点。对于 双连通分量A - 割点 - 双连通分量B,重构图成3个点 A - 割点 - B。
那么,所有的双连通分量缩完之后,新图G‘成了一棵树(若存在环,那么环里的点都在同一个双连通分量中,矛盾)
那么题目变成了:从X所在的G'中的点,到Y所在的G’中的点的路径中,有多少点是由割点变成的。
由于图G‘中的点都是 双连通分量 - 割点 - 双连通分量 - 割点 - 双连通分量……的形式(两个双连通分量不会连在一起)
那么X到Y的割点的数目就等于两点距离除以2,暨(dep[x] + dep[Y] - dep[LCA(X, Y)]) / 2,其中dep是深度,lca是最近公共祖先。
其中LCA用tarjan也好,用RMQ也好,随意。我用的是离线的tarjan。
 
细节:因为找边双连通分量的思路错了跪了一整天……写一下正确又优美的姿势是怎么样的>_<
类似于tarjan求强联通的算法,用一个vis[]数组记录某条边是否被访问过。
每次第一次访问一条边,把这条边压入栈中,在遍历完某条边指向的点之后,若pre[u] <= lowlink[v](见code),把栈中的边都设为同一个边双连通分量。
仔细想想觉得应该是对的。我不会证明>_<
 
PS:新图G’中点的数目可能会高达2*n的等级,试想一下原图恰好是一条链的情况,由于存在重边,边数也最好要2*m。
 
代码(178MS):
  1. #include <cstdio>
  2. #include <iostream>
  3. #include <algorithm>
  4. #include <cstring>
  5. #include <vector>
  6. using namespace std;
  7. typedef long long LL;
  8.  
  9. typedef pair<int, int> PII;
  10.  
  11. const int MAXV = ;
  12. const int MAXE = ;
  13.  
  14. int ans[MAXV];
  15. vector<PII> query[MAXV << ];
  16.  
  17. struct SccGraph {
  18. int head[MAXV << ], fa[MAXV << ], ecnt;
  19. bool vis[MAXV << ];
  20. int to[MAXE << ], next[MAXE << ];
  21. int dep[MAXV << ];
  22.  
  23. void init(int n) {
  24. memset(head, -, sizeof(int) * (n + ));
  25. memset(vis, , sizeof(bool) * (n + ));
  26. for(int i = ; i <= n; ++i) fa[i] = i;
  27. ecnt = ;
  28. }
  29.  
  30. int find_set(int x) {
  31. return fa[x] == x ? x : fa[x] = find_set(fa[x]);
  32. }
  33.  
  34. void add_edge(int u, int v) {
  35. to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt++;
  36. to[ecnt] = u; next[ecnt] = head[v]; head[v] = ecnt++;
  37. }
  38.  
  39. void lca(int u, int f, int deep) {
  40. dep[u] = deep;
  41. for(int p = head[u]; ~p; p = next[p]) {
  42. int &v = to[p];
  43. if(v == f || vis[v]) continue;
  44. lca(v, u, deep + );
  45. fa[v] = u;
  46. }
  47. vis[u] = true;
  48. for(vector<PII>::iterator it = query[u].begin(); it != query[u].end(); ++it) {
  49. if(vis[it->first]) {
  50. ans[it->second] = (dep[u] + dep[it->first] - * dep[find_set(it->first)]) / ;
  51. }
  52. }
  53. }
  54. } G;
  55.  
  56. int head[MAXV], lowlink[MAXV], pre[MAXV], ecnt, dfs_clock;
  57. int sccno[MAXV], scc_cnt;
  58. int to[MAXE], next[MAXE], scc_edge[MAXE];
  59. bool vis[MAXE], iscut[MAXV];
  60. int stk[MAXE], top;
  61. int n, m, q;
  62.  
  63. void init() {
  64. memset(head, -, sizeof(int) * (n + ));
  65. memset(pre, , sizeof(int) * (n + ));
  66. memset(iscut, , sizeof(bool) * (n + ));
  67. memset(vis, , sizeof(bool) * ( * m));
  68. ecnt = scc_cnt = dfs_clock = ;
  69. }
  70.  
  71. void add_edge(int u, int v) {
  72. to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt++;
  73. to[ecnt] = u; next[ecnt] = head[v]; head[v] = ecnt++;
  74. }
  75.  
  76. void tarjan(int u, int f) {
  77. pre[u] = lowlink[u] = ++dfs_clock;
  78. int child = ;
  79. for(int p = head[u]; ~p; p = next[p]) {
  80. if(vis[p]) continue;
  81. vis[p] = vis[p ^ ] = true;
  82. stk[++top] = p;
  83. int &v = to[p];
  84. if(!pre[v]) {
  85. ++child;
  86. tarjan(v, u);
  87. lowlink[u] = min(lowlink[u], lowlink[v]);\
  88. if(pre[u] <= lowlink[v]) {
  89. iscut[u] = true;
  90. ++scc_cnt;
  91. while(true) {
  92. int t = stk[top--];
  93. scc_edge[t] = scc_edge[t ^ ] = scc_cnt;
  94. if(t == p) break;
  95. }
  96. }
  97. } else lowlink[u] = min(lowlink[u], pre[v]);
  98. }
  99. if(f < && child == ) iscut[u] = false;
  100. }
  101.  
  102. void build() {
  103. G.init(scc_cnt);
  104. for(int p = ; p != ecnt; ++p) {
  105. int &v = to[p];
  106. if(iscut[v]) G.add_edge(sccno[v], scc_edge[p]);
  107. }
  108. }
  109.  
  110. void solve() {
  111. for(int i = ; i <= n; ++i)
  112. if(!pre[i]) tarjan(i, );
  113. for(int u = ; u <= n; ++u)
  114. if(iscut[u]) sccno[u] = ++scc_cnt;
  115. }
  116.  
  117. int main() {
  118. while(scanf("%d%d", &n, &m) != EOF) {
  119. if(n == && m == ) break;
  120. init();
  121. for(int i = ; i <= m; ++i) {
  122. int u, v;
  123. scanf("%d%d", &u, &v);
  124. add_edge(u, v);
  125. }
  126. solve();
  127. build();
  128. for(int i = ; i <= scc_cnt; ++i) query[i].clear();
  129. scanf("%d", &q);
  130. for(int i = ; i < q; ++i) {
  131. int x, y;
  132. scanf("%d%d", &x, &y);
  133. x = scc_edge[x * - ]; y = scc_edge[y * - ];
  134. query[x].push_back(make_pair(y, i));
  135. query[y].push_back(make_pair(x, i));
  136. }
  137. for(int i = ; i <= scc_cnt; ++i) if(!G.vis[i]) G.lca(i, , );
  138. for(int i = ; i < q; ++i) printf("%d\n", ans[i]);
  139. }
  140. }

HDU 3686 Traffic Real Time Query System(双连通分量缩点+LCA)(2010 Asia Hangzhou Regional Contest)的更多相关文章

  1. HDU 3686 Traffic Real Time Query System (图论)

    HDU 3686 Traffic Real Time Query System 题目大意 给一个N个点M条边的无向图,然后有Q个询问X,Y,问第X边到第Y边必需要经过的点有多少个. solution ...

  2. hdu 3686 Traffic Real Time Query System 点双两通分量 + LCA。这题有重边!!!

    http://acm.hdu.edu.cn/showproblem.php?pid=3686 我要把这题记录下来. 一直wa. 自己生成数据都是AC的.现在还是wa.留坑. 我感觉我现在倒下去床上就能 ...

  3. HDU 3686 Traffic Real Time Query System(点双连通)

    题意 ​ 给定一张 \(n\) 个点 \(m\) 条边的无向图,\(q\) 次询问,每次询问两边之间的必经之点个数. 思路 ​ 求两点之间必经之边的个数用的是边双缩点,再求树上距离.而对比边双和点双之 ...

  4. HDU 3685 Rotational Painting(多边形质心+凸包)(2010 Asia Hangzhou Regional Contest)

    Problem Description Josh Lyman is a gifted painter. One of his great works is a glass painting. He c ...

  5. HDU 3689 Infinite monkey theorem(DP+trie+自动机)(2010 Asia Hangzhou Regional Contest)

    Description Could you imaging a monkey writing computer programs? Surely monkeys are smart among ani ...

  6. 【HDOJ】3686 Traffic Real Time Query System

    这题做了几个小时,基本思路肯定是求两点路径中的割点数目,思路是tarjan缩点,然后以割点和连通块作为新节点见图.转化为lca求解.结合点——双连通分量与LCA. /* 3686 */ #includ ...

  7. POJ3694 Network(边双连通分量+缩点+LCA)

    题目大概是给一张图,动态加边动态求割边数. 本想着求出边双连通分量后缩点,然后构成的树用树链剖分+线段树去维护路径上的边数和..好像好难写.. 看了别人的解法,这题有更简单的算法: 在任意两点添边,那 ...

  8. 图论-桥/割点/双连通分量/缩点/LCA

    基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个 ...

  9. 【Targan+LCA】HDU 3686 Traffic Real Time Query

    题目内容 洛谷链接 给出一个\(n\)个节点,\(m\)条边的无向图和两个节点\(s\)和\(t\),问这两个节点的路径中有几个点必须经过. 输入格式 第一行是\(n\)和\(m\). 接下来\(m\ ...

随机推荐

  1. td的cellIndex属性被style.display改变

    IE7下面td的cellIndex属性,居然会随着style.display='none'的设置而改变,真是太恶心了

  2. ADO.NET连接到数据库(oracle)

    本文摘抄于http://www.cnblogs.com/luluping/archive/2009/10/13/1582737.html,如有侵权,请联系博主. OracleConnection 对象 ...

  3. sqlserver 中含有某字符串

    查找 sqlserver 中字符串的ascii码SET TEXTSIZE 0-- Create variables for the character string and for the curre ...

  4. swift-03-构造器(Designated&&Convenience)

    类里面所有的存储型属性--包括所有继承自父类的属性,都必须在构造过程中设置初始值.   构造器,为了确保所有类实例中的存储型属性都能获得初始值,设置了两个构造器--他们分别是指定构造器和便利构造器. ...

  5. Windows-004-显示文件的扩展名

    此文主要讲述 Windows 7 下如何显示文件的扩展名,敬请亲们参阅.若有不足之处,敬请大神指正,不胜感激! 通常安装好 Win7 后,文件的扩展名默认是不显示的,如下所示: 点击工具栏中的 组织, ...

  6. boost::circular_buffer

    boost::circular_buffer的push_back分析 circular_buffer为了效率考虑,使用了连续内存块保存元素   使用固定内存,没有隐式或者非期望的内存分配 快速在cir ...

  7. CentOS6.7搭建蜜罐dionaea

    yum -y install epel-release wget tar git autoconf* libtool-* mkdir /opt/dionaea 1.安装liblcfg软件.git cl ...

  8. 30天,APP创业从0到1【7.25上海站】

    活动概况 时间:2015年7月25日13:30-16:30 地点:太库•上海孵化器(张江金科路2889号长泰广场c座12楼) 主办:APICloud.诸葛.圣诺资讯 联合主办:微链.太库•上海孵化器 ...

  9. system执行shell命令

    system - execute a shell command #include <stdlib.h> int system (const char *command); 描述 syst ...

  10. Vue.2.0.5-事件处理器

    监听事件 可以用 v-on 指令监听 DOM 事件来触发一些 JavaScript 代码. 示例: <div id="example-1"> <button v- ...