POJ 3694 Network(tarjan+lca+并查集)

题目

给定一张 N N N个点 M M M条边的无向连通图,然后执行 Q Q Q次操作,每次向图中添加一条边,并且询问当前无向图中“桥”的数量。

题解

  • 先求出图中所有的边双,然后缩点
  • c [ x ] , c [ y ] c[x],c[y] c[x],c[y] x , y x,y x,y所属边双的编号
  • 询问时若 x , y x,y x,y同属一个e-DCC则割边数不变,若不在同一个边双内,缩点后的图变成了一棵树,树上的每一条边都为原图的割边,在 x , y x,y x,y间加边那么树上从 x x x y y y的路径上的所有边都不再是割边,答案只需减去这些边。
  • 对于统计 x , y x,y x,y路径上的边我们可以找 x , y x,y x,y l c a lca lca暴力往上跳,但这样的复杂度是不优秀的,我们可以并查集来维护,这样复杂度降为 O ( M + Q × N ) O(M+Q\times N) O(M+Q×N)

code

#include <bits/stdc++.h> 

using namespace std; 

template <class T> inline void read(T &s) {
	s = 0; T w = 1, ch = getchar(); 
	while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
	while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
	s *= w; 
}

const int N = 1e5 + 100; 
const int M = 4e5 + 100; 
const int Z = 21; 

int n, m, tot, tim, q, dcc, totc, T; 
int lin[N], dfn[N], low[N], c[N], linc[N], fa[N]; 
int f[N][Z], d[N]; 
bool bridge[M]; 
struct edge {
	int next, to; 
} e[M], ec[M]; 

inline void clean() {
	tot = 1, tim = 0, dcc = 0, totc = 1; 
	for (int i = 1; i <= n; ++i) 
		c[i] = lin[i] = linc[i] = dfn[i] = low[i] = 0; 
	memset(bridge, false, sizeof(bridge)); 
}

inline void add(int from, int to) {
	e[++tot].to = to; 
	e[tot].next = lin[from]; 
	lin[from] = tot; 
}

void tarjan(int u, int in_edge) {
	dfn[u] = low[u] = ++tim; 
	for (int i = lin[u]; i; i = e[i].next) {
		int v = e[i].to; 
		if (!dfn[v]) {
			tarjan(v, i); 
			low[u] = min(low[u], low[v]); 
			if (low[v] > dfn[u]) 
				bridge[i] = bridge[i ^ 1] = true; 
		}
		else if (i != (in_edge ^ 1)) 
			low[u] = min(low[u], dfn[v]); 
	}
}

void dfs(int u) {
	c[u] = dcc; 
	for (int i = lin[u]; i; i = e[i].next) {
		int v = e[i].to; 
		if (bridge[i] || c[v]) continue; 
		dfs(v); 
	}
}

inline void addc(int from, int to) {
	ec[++totc].to = to; 
	ec[totc].next = linc[from]; 
	linc[from] = totc; 
}

void bfs() {
	memset(d, 0, sizeof(d)); 
	memset(f, 0, sizeof(f)); 
	queue<int> q; 
	q.push(1); d[1] = 1; 
	while (!q.empty()) {
		int u = q.front(); q.pop(); 
		for (int i = linc[u]; i; i = ec[i].next) {
			int v = ec[i].to; 
			if (d[v]) continue; 
			d[v] = d[u] + 1; 
			f[v][0] = u; 
			for (int j = 1; j < 20; ++j) 
				f[v][j] = f[f[v][j - 1]][j - 1]; 
			q.push(v); 
		}
	}
}

int lca(int x, int y) {
	if (d[x] < d[y]) swap(x, y); 
	for (int i = 19; i >= 0; --i) {
		if (d[f[x][i]] >= d[y]) x = f[x][i]; 
	}
	if (x == y) return x; 
	for (int i = 19; i >= 0; --i) {
		if (f[x][i] != f[y][i]) 
			x = f[x][i], y = f[y][i]; 
	}
	return f[x][0]; 
}

inline int get(int x) {
	return fa[x] == x ? x : fa[x] = get(fa[x]); 
}

int main() {
	freopen("1.in", "r", stdin); 

	while (true) {
		read(n), read(m); 
		if (!n || !m) break; 
		clean(); 
		for (int i = 1; i <= m; ++i) {
			int x, y; read(x), read(y); 
			add(x, y); add(y, x); 
		}
		for (int i = 1; i <= n; ++i) {
			if (!dfn[i]) tarjan(i, 0); 
		}
		for (int i = 1; i <= n; ++i) {
			if (!c[i]) {
				++dcc, dfs(i); 
			}
		}
		for (int i = 2; i <= tot; ++i) {
			int x = e[i].to, y = e[i ^ 1].to; 
			if (c[x] == c[y]) continue; 
			addc(c[x], c[y]); 
		}
		bfs(); 
		for (int i = 1; i <= dcc; ++i) fa[i] = i; 
		int ans = dcc - 1; 
		read(q); 
		printf("Case %d:\n", ++T); 
		while (q--) {
			int x, y; read(x), read(y); 
			x = c[x], y = c[y]; 
			int p = lca(x, y); 
			x = get(x); 
			while (d[x] > d[p]) {
				fa[x] = f[x][0]; 
				ans--; 
				x = get(x); 
			}
			y = get(y); 
			while (d[y] > d[p]) {
				fa[y] = f[y][0]; 
				ans--; 
				y = get(y); 
			}
			printf("%d\n", ans); 
		}
		puts(""); 
	}
	return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值