poj3694 Network 求桥边个数[tarjan + LCA]

Network

又调试了好久,终于搞定了,又学习了一点点。。呵呵。

题意很简单:给定一个连通的无向图,有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 ;
}

 

你可能感兴趣的:(NetWork)