//有向图强连通分量:分量中任何两点均可通过路径互达,缩点后成为一个DAG
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int MaxN = 50000, MaxM = 200000;
int T, n, m, all, Timetot, Scctot;
int pre[MaxM + 5], last[MaxN + 5], other[MaxM + 5];
int pr[MaxM + 5], las[MaxN + 5], oth[MaxM + 5];
int dfn[MaxN + 5], low[MaxN + 5], col[MaxN + 5];
int ind[MaxN + 5], oud[MaxN + 5];
bool usedind[MaxN + 5], usedoud[MaxN + 5], usedused[MaxN + 5];
stack <int> sta;
vector <int> Scc[MaxN + 5];
map <pair<int, int>, bool> used;
void Build(int x, int y)
{
pre[++all] = last[x];
last[x] = all;
other[all] = y;
}
void Init()
{
all = -1; memset(last, -1, sizeof(last));
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
Build(u, v);
}
}
void Dfs_Scc(int x, int fa)
{
dfn[x] = low[x] = ++Timetot;
sta.push(x); //强连通分量 存点不存边
int ed = last[x], dr;
while (ed != -1) {
dr = other[ed];
if (!dfn[dr]) {
Dfs_Scc(dr, x);
low[x] = min(low[x], low[dr]);
}
else if (!col[dr] && dfn[dr] < dfn[x]) {
low[x] = min(low[x], dfn[dr]);
}
ed = pre[ed];
}
if (low[x] >= dfn[x]) { //强连通分量注意是while出来以后判
Scc[++Scctot].clear();
while (true) {
int u = sta.top(); sta.pop();
col[u] = Scctot;
Scc[Scctot].push_back(u);
if (u == x) break;
}
}
}
void rBuild(int x, int y)
{
pr[++all] = las[x];
las[x] = all;
oth[all] = y;
ind[x]++;
oud[y]++;
}
void ReBuildGraph()
{
used.clear();
all = -1; memset(las, -1, sizeof(las)); //all用原来的,las也记得清成-1
for (int i = 1; i <= n; i++) {
int ed = last[i], dr;
while (ed != -1) {
dr = other[ed];
if (col[i] != col[dr] && !used[make_pair(col[i], col[dr])]) {
used[make_pair(col[i], col[dr])] = true;
rBuild(col[i], col[dr]);
}
ed = pre[ed];
}
}
}
void Solve()
{
Timetot = Scctot = 0;
memset(dfn, 0, sizeof(dfn)); //dfn, low每次必须清空
memset(low, 0, sizeof(low));
memset(col, 0, sizeof(col));
memset(ind, 0, sizeof(ind));
memset(oud, 0, sizeof(oud));
for (int i = 1; i <= n; i++)
if (!dfn[i]) Dfs_Scc(i, -1);
ReBuildGraph();
memset(usedused, 0, sizeof(usedused));
int num = 0;
for (int i = 1; i <= n; i++) {
if (!usedused[col[i]]) {
usedused[col[i]] = true;
num++;
}
}
memset(usedind, 0, sizeof(usedind));
memset(usedoud, 0, sizeof(usedoud));
int sum_ind = 0, sum_oud = 0;
for (int i = 1; i <= n; i++) {
if (ind[col[i]] == 0 && !usedind[col[i]]) {
usedind[col[i]] = true;
sum_ind++;
}
if (oud[col[i]] == 0 && !usedoud[col[i]]) {
usedoud[col[i]] = true;
sum_oud++;
}
}
if (num == 1) printf("0\n");
else printf("%d\n", max(sum_ind, sum_oud));
}
int main()
{
scanf("%d", &T);
for (int cas = 1; cas <= T; cas++) {
Init();
Solve();
}
return 0;
}
强连通分量缩点
最新推荐文章于 2023-06-05 21:27:11 发布