scc 进阶
HDU-2767 Proving Equivalences
题意:给定
先缩点,然后就变成了一个 DAG。
然后要把 DAG 变成强连通,就把入度和出度为 0 的点头尾相连。
统计入度和出度,然后计算头尾,最大的就是答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int t, n, m, a[N], low[N], dfn[N], col[N], tim, scc, in[N], out[N];
bool vis[N];
vector<int> nbr[N];
stack<int> stk;
void tarjan(int u) {
vis[u] = 1;
stk.push(u);
dfn[u] = low[u] = ++tim;
for (auto v : nbr[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (vis[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
scc++;
while (!stk.empty()) {
vis[stk.top()] = 0;
col[stk.top()] = scc;
if (stk.top() == u) {
stk.pop();
break;
}
stk.pop();
}
}
return;
}
signed main() {
for (cin >> t; t--;) {
cin >> n >> m;
for (int i = 1; i <= n; i++)
nbr[i].clear();
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
nbr[u].push_back(v);
}
tim = scc = 0;
memset (low, 0, sizeof low);
memset (dfn, 0, sizeof dfn);
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
for (int i = 1; i <= scc; i++) {
in[i] = out[i] = 0;
}
for (int i = 1; i <= n; i++) {
for (int v : nbr[i]) {
if (col[i] != col[v]) {
in[col[v]] = out[col[i]] = 1;
}
}
}
int cnt1 = 0, cnt2 = 0;
for (int i = 1; i <= scc; i++) {
if (!in[i])
cnt1++;
if (!out[i])
cnt2++;
}
cout << (scc == 1 ? 0 : max(cnt1, cnt2)) << '\n';
}
return 0;
}
P2272 [ZJOI2007] 最大半连通子图
先缩点得到 DAG,对 DAG 求出其最长链长度与数量。
因为有重边,先去重,再拓扑 + dp。
设
当
否则,当
注意取模。
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N = 1e6 + 10;
struct Edge {
int u, v;
} edge[N];
int n, m, mod, ans, tim, scc, in[N], u[N], v[N], dfn[N], low[N], vis[N], val[N], dp[N], col[N], g[N];
stack<int> stk;
vector<int> nbr[N];
void tarjan(int u) {
vis[u] = 1;
stk.push(u);
dfn[u] = low[u] = ++tim;
for (auto v : nbr[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (vis[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
scc++;
while (!stk.empty()) {
vis[stk.top()] = 0;
col[stk.top()] = scc;
val[scc]++;
if (stk.top() == u) {
stk.pop();
break;
}
stk.pop();
}
}
return;
}
void topo() {
queue<int> q;
for (int i = 1; i <= scc; i++) {
if (!in[i])
q.push(i), dp[i] = val[i], g[i] = 1;
}
while (!q.empty()) {
int u = q.front();
q.pop();
ans = max(ans, dp[u]);
for (int v : nbr[u]) {
if (dp[v] == dp[u] + val[v]) {
g[v] = (g[v] + g[u]) % mod;
} else if (dp[v] < dp[u] + val[v]) {
dp[v] = dp[u] + val[v];
g[v] = g[u] % mod;
}
in[v]--;
if (in[v] == 0)
q.push(v);
}
}
return;
}
bool cmp(Edge A, Edge B) {
return A.u != B.u ? A.u < B.u : A.v < B.v;
}
signed main() {
cin >> n >> m >> mod;
for (int i = 1; i <= m; i++) {
cin >> u[i] >> v[i];
nbr[u[i]].pb(v[i]);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i])
tarjan(i);
}
int num = 0;
for (int i = 1; i <= m; i++)
if (col[u[i]] != col[v[i]])
edge[++num] = {col[u[i]], col[v[i]]};
sort (edge + 1, edge + 1 + num, cmp);
for (int i = 1; i <= n; i++)
nbr[i].clear();
for (int i = 1; i <= num; i++) {
if (edge[i].u == edge[i - 1].u && edge[i].v == edge[i - 1].v) {
continue;
}
nbr[edge[i].u].pb(edge[i].v);
in[edge[i].v]++;
}
topo();
cout << ans << '\n';
int res = 0;
for (int i = 1; i <= scc; i++) {
if (dp[i] == ans) {
res = (res + g[i]) % mod;
}
}
cout << res;
return 0;
}