标签:一个 size ons max 现在 break string bre using
在数学中,我们常常需要完成若干命题的等价性证明。
例如:有 \(4\) 个命题 \(a,b,c,d\),要证明他们是等价的,我们需要证明 \(a\Leftrightarrow b\),然后 \(b\Leftrightarrow c\),最后 \(c\Leftrightarrow d\)。注意每次证明是双向的,因此一共完成了 \(6\) 次推导。另一种证明方法是:证明 \(a\rightarrow b\),然后 \(b\rightarrow c\),接着 \(c\rightarrow d\),最后 \(d\rightarrow a\),只须 \(4\) 次证明。
现在你任务是证明 \(n\) 个命题全部等价,且你的朋友已经为你作出了 \(m\) 次推导(已知每次推导的内容),你至少还需做几次推导才能完成整个证明。
实际上这是一个图论题:给定一个有向图,问至少需要加几条边,可以使整个图变为一个 强连通图。
首先要知道,把边加到 同一个 \(\texttt{SCC}\) 中的某两点之间 没有任何意义,于是我们把做个图2做一个 缩点 的操作,使其成为一个 DAG。
然后我们就是在这些缩点后的点上加边。
显然一个 SCC 中,任意两点是互相可达的,于是所有点都必须有入度和出度。
若设入度为 \(0\) 的点集为 \(S\), 出度为 \(0\) 的点集为 \(T\),那么至少 \(|S| + |T|\) 条边必能构造处一个强连通图。
但我们可以“一边两用”:将尽可能多的边连成从 \(T\) 中的点到 \(S\) 中的点的边。这样答案就是 \(\max(|S|,|T|)\)。
缩点用 \(\texttt{Tarjan}\) 算法,复杂度 \(O(n + m)\)
#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\n", max(cnt1, cnt2));
}
signed main() {
int T;
scanf("%d", &T);
while (T--) solve();
return 0;
}
「UVA 12167」 Proving Equivalences
标签:一个 size ons max 现在 break string bre using
原文地址:https://www.cnblogs.com/-Wallace-/p/12829527.html