POJ 3694
题目链接:
http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=11132
题意:
给一个图,保证连通。
有q<1000个操作,每次加边。问每次操作后,图中剩余的桥数目。
思路:
图论题不要套版!图论题不要套板!图论题不要套板!
刚开始用双连通缩点,缩到后来越来越麻烦,因为更新节点之后更新新点时的指向总会变,而且需要每次都做一遍双连通分量和双连通缩点。不仅会TLE,还很麻烦,更做不出。
看题解。
缩点的本质是转化成缩点树,树边是原来的割边,也就是low[v]>pre[u]时刻的u和v的连边。故可以标记v,即标记了割边。
每次增加新边,相当于在缩点树上增加边。存在两种情况。一、在缩点中,不影响结果。二、不在同一缩点中,查找LCA然后去除对应树边。
题解采用一种简单的方法,按照原来没有缩点时的点找LCA,如果遇到割边即缩点树的树边,ans--。查找LCA用暴力法,设秩ranking,详见代码。
还有关于怎么算重边的问题。关键部分就是设一个f标记,判断当前v是不是fa并且是不是第一次遇到fa。是则把f标为0,这样,对于同一个元素,下次再遇到fa时,就可以用fa的pre值来更新自己的low值
源码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;
#define gmin(a,b) ((a) < (b) ? (a) : (b))
#define gmax(a,b) ((a) > (b) ? (a) : (b))
const int MAXN = 1e5 + 5;
int n, m;
vector<int>lin[MAXN];
int pa[MAXN], ranking[MAXN];
int low[MAXN], pre[MAXN], clock;
int ge[MAXN];
int ans;
void tarjan(int u, int fa)
{
int f = 1; ///去重边关键
pre[u] = low[u] = ++clock;
ranking[u] = ranking[pa[u]] + 1;
for(int i = 0 ; i < (int)lin[u].size() ; i++){
int v = lin[u][i];
if(pre[v] == 0){
pa[v] = u;
tarjan(v, u);
low[u] = gmin(low[u], low[v]);
if(low[v] > pre[u]){
ge[v] = 1;
ans++;
}
}
else if(f && v == fa) f = 0;
else
low[u] = gmin(low[u], pre[v]);
}
}
void LCA(int u, int v)
{
while(ranking[u] < ranking[v]){
// printf("v = %d\n", v);
if(ge[v]) ge[v] = 0, ans--;
v = pa[v];
}
while(ranking[v] < ranking[u]){
// printf("u = %d\n", u);
if(ge[u]) ge[u] = 0, ans--;
u = pa[u];
}
while(u != v){
// printf("u = %d, v = %d\n", u, v);
if(ge[u]) ge[u] = 0, ans--;
if(ge[v]) ge[v] = 0, ans--;
u = pa[u], v = pa[v];
}
}
int main()
{
int cas = 1;
while(scanf("%d%d", &n, &m) != EOF && n + m){
for(int i = 1 ; i <= n ; i++)
lin[i].clear();
int u, v;
for(int i = 1 ; i <= m ; i++){
scanf("%d%d", &u, &v);
lin[u].push_back(v);
lin[v].push_back(u);
}
for(int i = 1 ; i <= n ; i++)
pa[i] = i;
ans = 0;
memset(pre, 0, sizeof(pre));
memset(ge, 0, sizeof(ge));
memset(ranking, 0, sizeof(ranking));
clock = 0;
tarjan(1, -1);
// printf("bridge\n");
// for(int i = 1 ; i <= n ; i++){
// printf("i = %d, bri = %d, ranking = %d\n", i, ge[i], ranking[i]);
// }
// printf("ans = %d\n", ans);
int q;
scanf("%d", &q);
printf("Case %d:\n", cas++);
for(int i = 1; i <= q ; i++){
scanf("%d%d", &u, &v);
LCA(u, v);
printf("%d\n", ans);
}
printf("\n");
}
return 0;
}