POJ 3694 network LCA +tarjan求桥

题目链接(You can click it.)

题目含义:

有一张无向图,在其中添边。问每次添边之后,还剩多少桥。

思路:

首先,这个题解法不知这一种,这个简单一点,后续会补上其余的几个大同小异的解法(好题值得反复写)。

既然是求桥的数量,肯定离不开求桥了,求桥的数量可以在tarjan的过程中每遇到一个满足桥的判定式dfn < low,就ans++,最后ans就是初始桥的数量。或者也可以tarjan完毕后,缩点(dfs求连通块个数),最后的点数-1就是桥数。

题目中的图可以看作是一个无根树,人为的给树定一个根,将边打造为有向的,就可以得到一棵树,树上的点都是从父节点指向子节点。用一个mark数组,mark[i] 记录的是 i 节点指向父节点的边是否为桥。对于每一个输入进来的边 < u , v > <u,v> <u,v>,一定可以在树上找到一条边从 u u u v v v(题目中说了图是连通图,因此不存在两个点在树上找不到边),如果再增加一条边 < u , v > <u,v> <u,v>,那么原来从 u u u v v v路径上路过的桥边都失效了(这些边与新边形成了环),因此只需要找原路径上两点之间的边中的桥边的数量即可。

可以求两个点的 lca,让两个点同时向 lca 靠拢,路径上经过的边中的桥边即为所求。
tarjan 的过程中可以对每个点记录深度(毕竟tarjan也是对图进行了dfs),顺便标记每个点的父节点,再顺便求出来桥边的时候更新 mark 值。

这是参考一个大牛的解法,感觉这个解法是最简单的一个了QAQ

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define mem(a, b) memset(a, b, sizeof a)
const int N = 100100, M = 200100 * 2;
int head[N], to[M], nex[M], cnt;
int fa[N];
int dfn[N], low[N], num;
int d[N];
bool mark[N];
int ans;
void init(){
    
    
	ans = 0; cnt = 1; mem(head, 0); mem(nex, 0); mem(dfn, 0); 
	mem(low, 0); num = 0; mem(d, 0); mem(fa, -1); mem(mark, 0);
}
void add(int a, int b){
    
    
	++cnt;
	to[cnt] = b;
	nex[cnt] = head[a];
	head[a] = cnt;
}
void tarjan(int x, int ind, int depth){
    
    
	dfn[x] = low[x] = ++num;
	d[x] = depth;
	for (int i = head[x]; i; i = nex[i]){
    
    
		int y = to[i];
		if (!dfn[y]){
    
    
			fa[y] = x;
			tarjan(y, i, depth + 1);
			low[x] = min(low[x], low[y]);
			if (dfn[x] < low[y]){
    
    
				mark[y] = 1;
				ans++;
			}
		}
		else if (ind != (i ^ 1))low[x] = min(low[x], dfn[y]);
	}
}
int n, m, K;
void lca(int x, int y){
    
    
	if (d[x] < d[y]){
    
    
		x = x ^ y; y = x ^ y; x = x ^ y;
	}
	while (d[x] > d[y]){
    
    
		if (mark[x]){
    
    
			mark[x] = 0;
			ans--;
		}
		x = fa[x];
	}
	while (x != y){
    
    
		if (mark[x]){
    
    
			mark[x] = 0;
			ans--;
		}
		if (mark[y]){
    
    
			mark[y] = 0;
			ans--;
		}
		x = fa[x];
		y = fa[y];
	}
}
int main()
{
    
    
	freopen("in.in", "r", stdin);
	K = 1;
	while (~scanf("%d %d", &n, &m) && n && m){
    
    
		init();
		while (m--){
    
    
			int a, b;
			scanf("%d %d", &a, &b);
			add(a, b);
			add(b, a);
		}
		tarjan(1, 0, 1);
		int Q;
		cin >> Q;
		printf("Case %d:\n", K++);
		while (Q--){
    
    
			int x, y;
			scanf("%d %d", &x, &y);
			lca(x, y);
			printf("%d\n", ans);
		}
		puts("");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43701790/article/details/105316862
今日推荐