CF505D Mr. Kitayuta's Technology
CF505D Mr. Kitayuta's Technology
一道结论题。
记 \(M\) 个询问中提及的点数为 \(cnt\), 记这些点按照询问形成图的联通块个数为 \(K\),第 \(i\) 个联通块大小为 \(siz_i\)。
对于每个联通块,容易发现边数不是 \(siz_i-1\) 就是 \(siz_i\) 那么什么时候是\(siz_i-1\) 呢,是这个联通块不含强连通分量的时候。反之为 \(siz_i\),这个可以手动验证。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
typedef int ll;
const ll MAXN = 1e6+10;
stack <ll> st;
vector <ll> g[MAXN], g2[MAXN];
ll N, M, fa[MAXN], siz[MAXN], has[MAXN], num, dfn[MAXN], low[MAXN], vis[MAXN];
ll bel[MAXN], _N, tag1[MAXN], tag2[MAXN];
ll find_(ll);
void tarjan(ll);
int main() {
scanf("%d%d", &N, &M);
for (ll i = 1, x, y; i <= M; i++) {
scanf("%d%d", &x, &y);
tag1[x] = tag1[y] = 1;
g[x].push_back(y);
}
for (ll i = 1; i <= N; i++) if (!dfn[i]) tarjan(i);
for (ll i = 1; i <= N; i++) {
for (unsigned int j = 0; j < g[i].size(); j++) {
ll v = g[i][j];
if (bel[v] != bel[i]) g2[bel[i]].push_back(bel[v]);
else has[bel[i]] = 1;
}
if (tag1[i]) tag2[bel[i]] = 1;
}
for (ll i = 1; i <= _N; i++)
sort(g2[i].begin(), g2[i].end()), g2[i].erase(unique(g2[i].begin(), g2[i].end()), g2[i].end());
for (ll i = 1; i <= _N; i++) fa[i] = i;
for (ll i = 1; i <= _N; i++) {
for (unsigned int j = 0; j < g2[i].size(); j++) {
ll fx = find_(i), fy = find_(g2[i][j]);
if (fx != fy) {
fa[fx] = fy;
has[fy] |= has[fx];
siz[fy] += siz[fx];
}
}
}
ll ans = 0;
for (ll i = 1; i <= _N; i++) {
if (find_(i) == i && tag2[i]) {
ans += siz[i] - 1 + has[i];
}
}
printf("%d\n", ans);
return 0;
}
void tarjan(ll n) {
dfn[n] = low[n] = ++num;
st.push(n);
for (unsigned int i = 0; i < g[n].size(); i++) {
ll v = g[n][i];
if (vis[v]) continue;
if (dfn[v]) low[n] = min(low[n], dfn[v]);
else {
tarjan(v);
low[n] = min(low[n], low[v]);
}
}
if (dfn[n] == low[n]) {
ll t;
_N++;
do {
t = st.top();
st.pop();
bel[t] = _N;
vis[t] = 1;
siz[_N] += tag1[t];
} while (n != t);
}
}
ll find_(ll x) {return fa[x] == x ? x : fa[x] = find_(fa[x]);}
希望我们都有一个光明的未来