poj3694

tarjan缩点+lca

题目让求桥,先跑一遍tarjan,缩点并求出桥的数目,两点属于同一联通分量,那么输出桥的数目。如果不是,那么求出它们的lca,则两点上推至lca的所有点都不再是桥,因为成环了

话说没想到lca这么简单,虽说办法朴素了些,但是基于tarjan的那个我也学会了,它非常好的利用了dfs的性质,以后再整理吧

#include 
#include 
#include 
using namespace std;

const int N = 100100;
const int M = 200200;

int head[N], head2[N];
int dfn[N], low[N], father[N];
int deep[N];
int block[N], pre[N], st[N];
bool instack[N];
int tot, tot2, top, ord, sccnum, icase;

struct node{
	int next;
	int to;
	int id;
}edge[M * 4], edge2[M * 4];

void init(){
	memset(dfn, -1, sizeof(dfn));
	memset(head, -1, sizeof(head));
	memset(head2, -1, sizeof(head2));
	memset(low, 0, sizeof(low));
	memset(instack, 0, sizeof(instack));
	tot = tot2 = ord = sccnum = top = 0;
}
void addedge(int from, int to, int id){
	edge[tot]. to = to;
	edge[tot]. id = id;
	edge[tot]. next = head[from];
	head[from] = tot ++;
}
void addedge2(int from, int to){
	edge2[tot2]. to = to;
	edge2[tot2]. next = head2[from];
	head2[from] = tot2 ++;
}
void dfs(int u, int fa, int d){
	pre[u] = fa;
	deep[u] = d;
	for(int i = head2[u]; ~i; i = edge2[i]. next){
		int v = edge2[i]. to;
		if(v == fa)
			continue;
		dfs(v, u, d + 1);
	}
}
int find(int x){
	if(x == father[x])
		return x;
	return father[x] = find(father[x]);
}
void tarjan(int u, int x){
	dfn[u] = low[u] = ++ ord;
	instack[u] = true;
	st[top ++] = u;
	for(int i = head[u]; ~i; i = edge[i]. next){
		int v = edge[i]. to;
		if(edge[i]. id == x)
			continue;
		if(dfn[v] == -1){
			tarjan(v, edge[i]. id);
			low[u] = min(low[u], low[v]);
		}
		else if(instack[v]){
			low[u] = min(low[u], dfn[v]);
		}
	}
	int v;
	if(dfn[u] == low[u]){
		++ sccnum;
		do{
			v = st[-- top];
			instack[v] = false;
			block[v] = sccnum;
		}while(v != u);
	}
}
int LCA(int a, int b){
	while(a != b){
		if(deep[a] > deep[b])
			a = pre[a];
		else if(deep[a] < deep[b])
			b = pre[b];
		else{
			a = pre[a];
			b = pre[b];
		}
		a = find(a);
		b = find(b);
	}
	return a;
}
void solve(int n){
	tarjan(1, -1);
	for(int u = 1; u <= n; u ++){
		for(int i = head[u]; ~i; i = edge[i]. next){
			int v = edge[i]. to;
			if(block[u] == block[v])
				continue;
			addedge2(block[u], block[v]);
		}
	}
	for(int i = 1; i <= sccnum; i ++)
		father[i] = i;
	int cnt = sccnum - 1;
	dfs(1, -1, 0);
	int q, a, b, lca;
	scanf("%d", &q);
	printf("Case %d:\n", icase++);
	while(q --){
		scanf("%d %d", &a, &b);
		a = block[a];
		b = block[b];
		if(a == b){
			printf("%d\n", cnt);
			continue;
		}
		a = find(a);
		b = find(b);
		lca = LCA(a, b);
		int x = 0;
		while(a != lca){
			x ++;
			father[a] = lca;
			a =  pre[a];
			a = find(a);
		}
		while(b != lca){
			x ++;
			father[b] = lca;
			b = pre[b];
			b = find(b);
		}
		cnt -= x;
		printf("%d\n", cnt);
	}
}
int main(){
	int n, m;
	int u, v;
	icase = 1;
	while(~scanf("%d %d", &n, &m)){
		if(!n && !m)
			break;
		init();
		for(int i = 1; i <= m; i ++){
			scanf("%d %d", &u, &v);
			addedge(u, v, i);
			addedge(v, u, i);
		}
		solve(n);
		printf("\n");
	}
	return 0;
}

 

你可能感兴趣的:(poj3694)