HDU3394 Railway 题解(边双连通分量)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3394
题目大意:
给定一个无向图,如果从一个点出发经过一些点和边能回到该点本身,那么一路走过来的这些点和边的集合就是一个环。
一个公园中有 n 个景点,景点之间通过无向的道路来连接,如果至少两个环公用一条路,路上的游客就会发生冲突;如果一条路不属于任何的环,这条路就没必要修。
问,有多少路不必修,有多少路会发生冲突?
解题思路:
每一个连通块中,如果边数大于点数,这个块中所有的边全部是冲突边。
所有桥为不需要修建的路。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010, maxm = 100010;
struct Edge {
int u, v, nxt;
Edge () {};
Edge (int _u, int _v, int _nxt) { u = _u; v = _v; nxt = _nxt; }
} edge[maxm<<1];
int n, m, head[maxn], ecnt;
void init() {
memset(head, -1, sizeof(int)*(n+1));
ecnt = 0;
}
void addedge(int u, int v) {
edge[ecnt] = Edge(u, v, head[u]); head[u] = ecnt ++;
edge[ecnt] = Edge(v, u, head[v]); head[v] = ecnt ++;
}
int dfn[maxn], low[maxn], cnt, bridge_num, crash_num;
stack<int> stk;
set<int> bcc;
void tarjan(int u, int pre) {
dfn[u] = low[u] = ++cnt;
for (int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].v;
if (v == pre) continue;
if (!dfn[v]) {
stk.push(i);
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
int id;
int tmp_cnt = 0;
bcc.clear();
do {
tmp_cnt ++;
id = stk.top();
stk.pop();
bcc.insert(edge[id].u);
bcc.insert(edge[id].v);
} while (edge[id].u != u || edge[id].v != v);
if (tmp_cnt > bcc.size()) crash_num += tmp_cnt;
}
if (low[v] > dfn[u]) bridge_num ++;
}
else if (dfn[v] < dfn[u]) {
stk.push(i);
low[u] = min(low[u], dfn[v]);
}
}
}
int main() {
while (~scanf("%d%d", &n, &m) && n) {
init();
memset(dfn, 0, sizeof(int)*(n+1));
cnt = bridge_num = crash_num = 0;
while (m --) {
int a, b;
scanf("%d%d", &a, &b);
a ++; b ++;
addedge(a, b);
}
for (int i = 1; i <= n; i ++)
if (!dfn[i]) tarjan(i, -1);
printf("%d %d\n", bridge_num, crash_num);
}
return 0;
}