「UVA 12167」 Proving Equivalences

Posted -wallace-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「UVA 12167」 Proving Equivalences相关的知识,希望对你有一定的参考价值。

Description

在数学中,我们常常需要完成若干命题的等价性证明。

例如:有 (4) 个命题 (a,b,c,d),要证明他们是等价的,我们需要证明 (aLeftrightarrow b),然后 (bLeftrightarrow c),最后 (cLeftrightarrow d)。注意每次证明是双向的,因此一共完成了 (6) 次推导。另一种证明方法是:证明 (a ightarrow b),然后 (b ightarrow c),接着 (c ightarrow d),最后 (d ightarrow a),只须 (4) 次证明。

现在你任务是证明 (n) 个命题全部等价,且你的朋友已经为你作出了 (m) 次推导(已知每次推导的内容),你至少还需做几次推导才能完成整个证明。

Hint

  • (1le nle 2 imes 10^4)
  • (0le mle 5 imes 10^4)

Solution

实际上这是一个图论题:给定一个有向图,问至少需要加几条边,可以使整个图变为一个 强连通图

首先要知道,把边加到 同一个 ( exttt{SCC}) 中的某两点之间 没有任何意义,于是我们把做个图2做一个 缩点 的操作,使其成为一个 DAG。

然后我们就是在这些缩点后的点上加边。

显然一个 SCC 中,任意两点是互相可达的,于是所有点都必须有入度和出度。

若设入度为 (0) 的点集为 (S), 出度为 (0) 的点集为 (T),那么至少 (|S| + |T|) 条边必能构造处一个强连通图。

但我们可以“一边两用”:将尽可能多的边连成从 (T) 中的点到 (S) 中的点的边。这样答案就是 (max(|S|,|T|))

缩点用 ( exttt{Tarjan}) 算法,复杂度 (O(n + m))

Code

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>

using namespace std;
const int N = 2e5 + 5;

vector<int> G[N];
int n, m, belong[N];

vector<int> Gx[N];
int scc, in[N], out[N];

int dfn[N], low[N];
int timer;
stack<int> st;
bool inst[N];

void Tarjan(int x) {
	dfn[x] = low[x] = ++timer;
	st.push(x), inst[x] = true;
	for (auto y : G[x])
		if (!dfn[y]) Tarjan(y), low[x] = min(low[x], low[y]);
		else if (inst[y]) low[x] = min(low[x], dfn[y]);
	
	if (dfn[x] == low[x])
		for (++scc; ; ) {
			int k = st.top(); st.pop();
			inst[k] = false, belong[k] = scc;
			if (x == k) break;
		}
}

void solve() {
	memset(dfn, 0, sizeof dfn);
	memset(low, 0, sizeof low);
	memset(inst, 0, sizeof inst);
	memset(in, 0, sizeof in);
	memset(out, 0, sizeof out);
	memset(belong, 0, sizeof belong);
	timer = scc = 0, st = stack<int>();
	
	scanf("%d%d", &n, &m);
	for (register int i = 1; i <= n; i++)
		G[i].clear(), Gx[i].clear();
	for (register int u, v, i = 1; i <= m; i++) {
		scanf("%d%d", &u, &v);
		G[u].push_back(v);
	}
	
	for (register int i = 1; i <= n; i++)
		if (!dfn[i]) Tarjan(i);
	
	if (scc == 1) return void(puts("0"));
	
	for (register int i = 1; i <= n; i++)
		for (auto j : G[i])
			if (belong[i] != belong[j])
				Gx[belong[i]].push_back(belong[j]);
	
	for (register int i = 1; i <= scc; i++) {
		out[i] = Gx[i].size();
		for (auto j : Gx[i]) in[j]++;	
	}
	int cnt1 = 0, cnt2 = 0;
	for (register int i = 1; i <= scc; i++)
		cnt1 += (in[i] == 0), cnt2 += (out[i] == 0);
	
	printf("%d
", max(cnt1, cnt2));
}

signed main() {
	int T;
	scanf("%d", &T);
	while (T--) solve();
	return 0;
}

以上是关于「UVA 12167」 Proving Equivalences的主要内容,如果未能解决你的问题,请参考以下文章

Proving Equivalences

HDU 2767 Proving Equivalences

Proving Equivalences (LA 4287)

HDU 2767 Proving Equivalences (Tarjan縮點)

HDU 2767 Proving Equivalences (Tarjan)

POJ3045--Cow Acrobats(theory proving)