又调试了好久,终于搞定了,又学习了一点点。。呵呵。
题意很简单:给定一个连通的无向图,有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;
}