又调试了好久,终于搞定了,又学习了一点点。。呵呵。
题意很简单:给定一个连通的无向图,有Q组操作,每组add一条边(u, v),问图中现有多少条割边。
思路:首先是一个tarjan,求的强连通分支和桥边,然后缩图,这样就得到一棵树,缩图的时候注意一下,将点分层,而且是个有根树,这样对于每增加一条边,就会形成一个环,那么环上的所有边都不是割边了,求的时候,用LCA的方法,分别将bel[u],bel[v]到最近公共祖先结点路径上的边都标志为非割边,这样就可以很快搞定了。
7816618 | ylfdrib | 3694 | Accepted | 10908K | 391MS | C++ | 3176B | 2010-11-01 09:51:50 |
// ============================================================================
// Name : poj3694.cpp
// Author : birdfly
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
// ============================================================================
#include < iostream >
#include < stdio.h >
#include < string .h >
#define NN 100010
using namespace std;
typedef struct node{
int v;
int vis;
struct node * nxt;
struct node * op;
}NODE;
NODE * Link[NN];
NODE edg[NN * 4 ];
int idx, N, M, scc, time, top;
int bel[NN];
int inSta[NN];
int stk[NN];
int dfn[NN]; // 发现时间标号
int low[NN];
int lev[NN]; // 记录每个分支的深度
int mark[NN];
int fa[NN]; // 记录父亲结点
int brig[NN]; // 记录桥边,brig[i] 表示边(fa[i], i)是桥边
void Add( int u, int v){
edg[idx].v = v;
edg[idx].vis = 0 ;
edg[idx].nxt = Link[u];
edg[idx].op = edg + idx + 1 ;
Link[u] = edg + idx ++ ;
edg[idx].v = u;
edg[idx].vis = 0 ;
edg[idx].nxt = Link[v];
edg[idx].op = edg + idx - 1 ;
Link[v] = edg + idx ++ ;
}
void Init(){
memset(Link, 0 , sizeof (Link));
memset(inSta, 0 , sizeof (inSta));
memset(dfn, 0 , sizeof (dfn));
idx = scc = 0 ;
top = time = 0 ;
}
void dfs( int u){ // tarjan过程,查找强连通分支和桥
int v;
dfn[u] = ++ time;
low[u] = dfn[u];
inSta[u] = 1 ;
stk[ ++ top] = u;
for (NODE * p = Link[u]; p; p = p -> nxt){
if ( ! p -> vis){
p -> vis = p -> op -> vis = 1 ;
if ( ! dfn[p -> v]){
dfs(p -> v);
if (low[u] > low[p -> v]){
low[u] = low[p -> v];
}
} else if (inSta[p -> v]){
if (low[u] > dfn[p -> v]){
low[u] = dfn[p -> v];
}
}
}
}
if (low[u] == dfn[u]){
scc ++ ;
do {
v = stk[top -- ];
bel[v] = scc;
inSta[v] = 0 ;
} while (v != u);
}
}
void creattree( int u, int l){ // 深搜的时候,将图按照强连通分支缩成一棵有根树
lev[bel[u]] = l;
for (NODE * p = Link[u]; p; p = p -> nxt){
if (mark[p -> v]) continue ;
mark[p -> v] = 1 ;
if ( ! brig[bel[p -> v]] && bel[u] != bel[p -> v] && low[p -> v] == dfn[p -> v]){
brig[bel[p -> v]] = 1 ;
fa[bel[p -> v]] = bel[u];
creattree(p -> v, l + 1 );
} else creattree(p -> v, l); /// //
}
}
void lca( int x, int y){ // lca求得最近公共祖先,同时更改桥边
if (lev[x] > lev[y]){
x ^= y;
y ^= x;
x ^= y;
}
while (lev[x] < lev[y]){
if (brig[y]){
scc -- ;
brig[y] = 0 ;
}
y = fa[y];
}
while (x != y){
if (brig[x]){scc -- ; brig[x] = 0 ;}
if (brig[y]){scc -- ; brig[y] = 0 ;}
x = fa[x]; y = fa[y];
}
}
int main() {
int a, b, Q, x, y;
int ca = 1 ;
while (scanf( " %d%d " , & N, & M) != EOF){
if (N == 0 && M == 0 ) break ;
printf( " Case %d:\n " , ca ++ );
Init();
while (M -- ){
scanf( " %d%d " , & a, & b);
Add(a, b);
}
dfs( 1 );
memset(mark, 0 , sizeof ( int ) * (N + 1 ));
memset(brig, 0 , sizeof ( int ) * (scc + 1 ));
// int i;
// for (i = 1; i <= N; i++) printf("%d\n", bel[i]);
mark[ 1 ] = 1 ;
creattree( 1 , 1 );
// for(i = 1; i <= scc; i++) printf("%d\n", fa[i]);
// printf("%d\n", scc);
scanf( " %d " , & Q);
while (Q -- ){
scanf( " %d%d " , & a, & b);
if (scc == 1 || bel[a] == bel[b]){
printf( " %d\n " , scc - 1 );
} else {
x = bel[a];
y = bel[b];
lca(x, y);
printf( " %d\n " , scc - 1 );
}
}
puts( "" );
}
return 0 ;
}