【连通图|边双连通分量+Tarjan+并查集】POJ-3694 Network(400+ms)
Network
Time Limit: 5000MS Memory Limit: 65536K
Description
A network administrator manages a large network. The network consists of N computers and M links between pairs of computers. Any pair of computers are connected directly or indirectly by successive links, so data can be transformed between any two computers. The administrator finds that some links are vital to the network, because failure of any one of them can cause that data can’t be transformed between some computers. He call such a link a bridge. He is planning to add some new links one by one to eliminate all bridges.
You are to help the administrator by reporting the number of bridges in the network after each new link is added.
Input
The input consists of multiple test cases. Each test case starts with a line containing two integers N(1 ≤ N ≤ 100,000) and M(N - 1 ≤ M ≤ 200,000).
Each of the following M lines contains two integers A and B ( 1≤ A ≠ B ≤ N), which indicates a link between computer A and B. Computers are numbered from 1 to N. It is guaranteed that any two computers are connected in the initial network.
The next line contains a single integer Q ( 1 ≤ Q ≤ 1,000), which is the number of new links the administrator plans to add to the network one by one.
The i-th line of the following Q lines contains two integer A and B (1 ≤ A ≠ B ≤ N), which is the i-th added new link connecting computer A and B.
The last test case is followed by a line containing two zeros.
Output
For each test case, print a line containing the test case number( beginning with 1) and Q lines, the i-th of which contains a integer indicating the number of bridges in the network after the first i new links are added. Print a blank line after the output for each test case.
Sample Input
3 2
1 2
2 3
2
1 2
1 3
4 4
1 2
2 1
2 3
1 4
2
1 2
3 4
0 0
Sample Output
Case 1:
1
0
Case 2:
2
0
Source
2008 Asia Hefei Regional Contest Online by USTC
题意: 给出一个无向连通图,添加若干条边,每添加一条边后输出图中剩余的桥的数量。
思路: 首先是要跑一遍tarjan算法,得出图中桥的个数。在添加边的时候,例如添加u->v的边,如果该边属于一个边双连通分量当中,那么和没有添加是一样的对桥不会造成影响。但是如果不是同一个边双连通分量,那么会影响到u和v的最近公共祖先到u和v这两条路上的所有的桥,它们全都不再是桥了。
现在有了一个Tarjan+缩点+LCA的思路。
目前我还不会LCA的在线算法,但是连通分量中的Tarjan算法真的是个好东西。利用它生成的dfn数组,可以直接求出lca。在dfs的过程中,按照dfs序给每个结点打上了时间戳dfn,同时记录每个结点的父结点。
在Tarjan的时候,如果同属于一个边双连通分量就用并查集并起来,这样查询的时候就可以之间判断是否属于同一个边双连通分量了。
如果分属于不同的分量,就先将时间戳靠下的结点(例如v)一步一步向上合并直到v的时间戳在u的上面,然后再将u向上合并直到u = v。此时u就是lca。
合并过程中一旦遇到桥就自减1.
代码如下:
/*
* ID: j.sure.1
* PROG:
* LANG: C++
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <climits>
#include <iostream>
#define PB push_back
#define LL long long
using namespace std;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
/****************************************/
const int N = 1e5 + 5, M = 4e5 + 5;
struct Edge {
int v, next;
Edge(){}
Edge(int _v, int _next):
v(_v), next(_next){}
}e[M];
int n, m, deep, tot;
int dfn[N], head[N], fa[N], pre[N];
int bridge;
void init()
{
deep = tot = bridge = 0;
for(int i = 1; i <= n; i++) fa[i] = i;
memset(head, -1, sizeof(head));
memset(pre, 0, sizeof(pre));
memset(dfn, 0, sizeof(dfn));
}
int Find(int x)
{
if(x != fa[x]) return fa[x] = Find(fa[x]);
return x;
}
bool Union(int x, int y)
{
int fx = Find(x), fy = Find(y);
if(fx != fy) {
fa[fy] = fx;
return true;
}
return false;
}
void add(int u, int v)
{
e[tot] = Edge(v, head[u]);
head[u] = tot++;
e[tot] = Edge(u, head[v]);
head[v] = tot++;
}
int dfs(int u, int pa)
{
int lowu = dfn[u] = ++deep;
for(int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(!dfn[v]) {
pre[v] = u;
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if(lowv > dfn[u]) bridge++;
else Union(u, v);//如果不是桥,那就属于同一个边双连通分量
}
else if(dfn[v] < dfn[u] && v != pa) {
lowu = min(lowu, dfn[v]);
}
}
return lowu;
}
int lca(int u, int v)
{
if(Find(u) == Find(v)) return bridge;
if(dfn[u] > dfn[v]) swap(u, v);
while(dfn[u] < dfn[v]) {
if(Union(pre[v], v)) bridge--;
v = pre[v];
}//从下至上一步一步沿着v的父亲进行合并直到公共祖先
while(u != v) {
if(Union(u, pre[u])) bridge--;
u = pre[u];
}
return bridge;
}
int main()
{
#ifdef J_Sure
freopen("000.in", "r", stdin);
//freopen("999.out", "w", stdout);
#endif
int cas = 1;
while(scanf("%d%d", &n, &m), n||m) {
init();
int u, v;
for(int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
add(u, v);
}
pre[1] = 1;
dfs(1, 1);
int op;
scanf("%d", &op);
printf("Case %d:\n", cas++);
for(int i = 0; i < op; i++) {
scanf("%d%d", &u, &v);
printf("%d\n", lca(u, v));
}
puts("");
}
return 0;
}
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。