POJ 3694 Network (tarjan + LCA)

时间:2023-03-10 06:29:27
POJ 3694 Network (tarjan + LCA)

题目链接:http://poj.org/problem?id=3694

题意是给你一个无向图n个点,m条边,将m条边连接起来之后形成一个图,有Q个询问,问将u和v连接起来后图中还有多少个桥。

首先用tarjan标记点的low和dfn值,那么u和v相连的边是桥的条件是dfn[u] < low[v](说明v与u不在一个连通分量里面,v无法通过回溯到达u点,画个图模拟会清楚)。那么bridge[v]++表示u与v相连的边是桥(若是标记bridge[u]++,则最后的答案可能会出错,亲测)。要是u和v相连,不属于同一个连通分量的话会形成一个环路,那么环路里所有的桥都没有了,所以用LCA将u和v一边找公共祖节点,一边消除桥。

代码如下:

 #include <iostream>
#include <cstdio>
#include <cstring> using namespace std;
const int MAXN = 2e5 + ;
struct data {
int next , to;
}edge[MAXN * ];
int head[MAXN] , block[MAXN] , low[MAXN] , dfn[MAXN] , st[MAXN] , dep[MAXN] , par[MAXN] , bridge[MAXN];
int top , ord , sccnum , cont , ans;
bool instack[MAXN]; void init() {
memset(head , - , sizeof(head));
memset(low , , sizeof(low));
memset(dfn , , sizeof(dfn));
memset(instack , false , sizeof(instack));
memset(bridge , , sizeof(bridge));
top = ord = sccnum = cont = ans = ;
} inline void add(int u , int v) {
edge[cont].next = head[u];
edge[cont].to = v;
head[u] = cont++;
} void tarjan(int u , int p , int d) {
low[u] = dfn[u] = ++ord;
st[++top] = u;
instack[u] = true;
par[u] = p;
dep[u] = d;
for(int i = head[u] ; ~i ; i = edge[i].next) {
int v = edge[i].to;
if(v == p)
continue;
if(!dfn[v]) {
tarjan(v , u , d + );
low[u] = min(low[v] , low[u]);
if(dfn[u] < low[v]) {
bridge[v]++;
ans++;
}
}
else if(instack[v]) {
low[u] = min(low[u] , dfn[v]);
}
}
if(low[u] == dfn[u]) {
int v;
sccnum++;
do {
v = st[top--];
instack[v] = false;
block[v] = sccnum;
}while(u != v);
}
} void lca(int u , int v) {
while(dep[u] < dep[v]) {
if(bridge[v]) {
ans--;
bridge[v]--;
}
v = par[v];
}
while(dep[u] > dep[v]) {
if(bridge[u]) {
ans--;
bridge[u]--;
}
u = par[u];
}
while(v != u) {
if(bridge[u]) {
bridge[u]--;
ans--;
}
if(bridge[v]) {
bridge[v]--;
ans--;
}
u = par[u];
v = par[v];
}
} int main()
{
int n , m , u , v , q , Case = ;
while(~scanf("%d %d" , &n , &m) && (n || m)) {
init();
while(m--) {
scanf("%d %d" , &u , &v);
add(u , v);
add(v , u);
}
tarjan( , - , );
scanf("%d" , &q);
printf("Case %d:\n" , Case++);
while(q--) {
scanf("%d %d" , &u , &v);
if(block[u] == block[v]) {
printf("%d\n" , ans);
continue;
}
lca(u , v);
printf("%d\n" , ans);
}
putchar('\n');
}
}