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):
 #include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL; typedef pair<int, int> PII; const int MAXV = ;
const int MAXE = ; int ans[MAXV];
vector<PII> query[MAXV << ]; struct SccGraph {
int head[MAXV << ], fa[MAXV << ], ecnt;
bool vis[MAXV << ];
int to[MAXE << ], next[MAXE << ];
int dep[MAXV << ]; void init(int n) {
memset(head, -, sizeof(int) * (n + ));
memset(vis, , sizeof(bool) * (n + ));
for(int i = ; i <= n; ++i) fa[i] = i;
ecnt = ;
} int find_set(int x) {
return fa[x] == x ? x : fa[x] = find_set(fa[x]);
} void add_edge(int u, int v) {
to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt++;
to[ecnt] = u; next[ecnt] = head[v]; head[v] = ecnt++;
} void lca(int u, int f, int deep) {
dep[u] = deep;
for(int p = head[u]; ~p; p = next[p]) {
int &v = to[p];
if(v == f || vis[v]) continue;
lca(v, u, deep + );
fa[v] = u;
}
vis[u] = true;
for(vector<PII>::iterator it = query[u].begin(); it != query[u].end(); ++it) {
if(vis[it->first]) {
ans[it->second] = (dep[u] + dep[it->first] - * dep[find_set(it->first)]) / ;
}
}
}
} G; int head[MAXV], lowlink[MAXV], pre[MAXV], ecnt, dfs_clock;
int sccno[MAXV], scc_cnt;
int to[MAXE], next[MAXE], scc_edge[MAXE];
bool vis[MAXE], iscut[MAXV];
int stk[MAXE], top;
int n, m, q; void init() {
memset(head, -, sizeof(int) * (n + ));
memset(pre, , sizeof(int) * (n + ));
memset(iscut, , sizeof(bool) * (n + ));
memset(vis, , sizeof(bool) * ( * m));
ecnt = scc_cnt = dfs_clock = ;
} void add_edge(int u, int v) {
to[ecnt] = v; next[ecnt] = head[u]; head[u] = ecnt++;
to[ecnt] = u; next[ecnt] = head[v]; head[v] = ecnt++;
} void tarjan(int u, int f) {
pre[u] = lowlink[u] = ++dfs_clock;
int child = ;
for(int p = head[u]; ~p; p = next[p]) {
if(vis[p]) continue;
vis[p] = vis[p ^ ] = true;
stk[++top] = p;
int &v = to[p];
if(!pre[v]) {
++child;
tarjan(v, u);
lowlink[u] = min(lowlink[u], lowlink[v]);\
if(pre[u] <= lowlink[v]) {
iscut[u] = true;
++scc_cnt;
while(true) {
int t = stk[top--];
scc_edge[t] = scc_edge[t ^ ] = scc_cnt;
if(t == p) break;
}
}
} else lowlink[u] = min(lowlink[u], pre[v]);
}
if(f < && child == ) iscut[u] = false;
} void build() {
G.init(scc_cnt);
for(int p = ; p != ecnt; ++p) {
int &v = to[p];
if(iscut[v]) G.add_edge(sccno[v], scc_edge[p]);
}
} void solve() {
for(int i = ; i <= n; ++i)
if(!pre[i]) tarjan(i, );
for(int u = ; u <= n; ++u)
if(iscut[u]) sccno[u] = ++scc_cnt;
} int main() {
while(scanf("%d%d", &n, &m) != EOF) {
if(n == && m == ) break;
init();
for(int i = ; i <= m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
add_edge(u, v);
}
solve();
build();
for(int i = ; i <= scc_cnt; ++i) query[i].clear();
scanf("%d", &q);
for(int i = ; i < q; ++i) {
int x, y;
scanf("%d%d", &x, &y);
x = scc_edge[x * - ]; y = scc_edge[y * - ];
query[x].push_back(make_pair(y, i));
query[y].push_back(make_pair(x, i));
}
for(int i = ; i <= scc_cnt; ++i) if(!G.vis[i]) G.lca(i, , );
for(int i = ; i < q; ++i) printf("%d\n", ans[i]);
}
}

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. spring容器IOC创建对象<三>

    问题?Spring的DI讲解.DI有几种注入方式.一.spring的DI:依赖注入给属性赋值DI定义:一个对象类的属性可以使用springDI(依赖注入)来进行赋值,但是并不是所有的类属性都适合spr ...

  2. iOS开发入门教程

    iOS开发入门教程 http://my.oschina.net/mailzwj/blog/133273 摘要 iOS开发入门教程,从创建项目到运行项目,包括OC基础,调试,模拟器设置等相关知识. iO ...

  3. ubuntu如何开启root,如何启用Ubuntu中root帐号

    jingyan.baidu.com/article/495ba84116104238b20ede62.html ubuntu如何开启root,如何启用Ubuntu中root帐号 | 浏览:8344 | ...

  4. 文件对比工具Beyond Compare使用方法

    今天向大家介绍一个使用起来十分方便且功能十分强大的文件对比工具-Beyond Compare. 1    工具下载 工具的下载很简单,百度搜索Beyond Compare即可. 下载完成后,解压缩,双 ...

  5. Linux学习之CentOS(十)--虚拟机下的CentOS如何上网

    原地址:http://www.cnblogs.com/xiaoluo501395377/archive/2013/04/05/3001148.html 这篇随笔应该说跟CentOS的学习关系不是很大, ...

  6. Linux进程间通信与线程间同步详解(全面详细)

    引用:http://community.csdn.net/Expert/TopicView3.asp?id=4374496linux下进程间通信的几种主要手段简介: 1. 管道(Pipe)及有名管道( ...

  7. 【C++】字符串的大小写转换

    #include<string> #include<algorithm> #include<iostream> using namespace std; int m ...

  8. 免费真机调试 -- Xcode7以上版本

    刚新安装了Xcode7 , 据说这个版本可以免费真机调试,于是用了一个新的AppID测试了,发现真的可以免费真机调试了呢!新的appId账号(随便一个苹果手机账号就行),没有支付每年的99美刀,也没有 ...

  9. action script 3如何检测播放器域

    检测播放器域   用户在上面观看媒体内容的网页的 URL 和域并非始终随时可用.如果托管网站允许,您可使用 ExternalInterface 类获取确切 URL.尽管如此,允许第三方视频播放器的一些 ...

  10. 在Fedora8上配置Tomcat6.0.37

    说是配置,其实很简单,解压拷贝是主要的活动,主要还是Tomcat做得很好很方便. 安装Tomcat的前提是把JDK装好,这和Win下是一样的.下面是整个步骤: 第一步,到Tomcat官网下载apach ...