「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 (LA 4287)
HDU 2767 Proving Equivalences (Tarjan縮點)