这道题是问最少添加几条边使得整个图强连通。首先,我们知道强连通是一个等价的关系,那么我们可以将强连通分量缩成一个点,即这个强连通分量的编号,这个过程称为强连通缩点。缩点后图变成一个DAG(有向无环),只需统计出度和入度为0的点各有多少,然后取大。这很好理解,因为我们添加边是从一个出度为0的点连到入度为0的点,这样就可以使整个图强连通。具体实现用的是Tarjan的算法= =。
#include <stack>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 20010;
const int M = 50010;
stack <int> S;
int n,m;
int tot, head[N];
int in[N],out[N];
int dfs_clock, point, vis[N], pre[N], low[N], belong[N];
struct edge{
int v,next;
}node[M];
void init(){
while(!S.empty()) S.pop();
tot = dfs_clock = point = 0;
for(int i=1; i<=n; i++){
head[i] = -1;
vis[i] = 0;
pre[i] = 0;
out[i] = 0;
in[i] = 0;
}
}
void addedge(int u, int v){
node[tot].v = v;
node[tot].next = head[u];
head[u] = tot++;
}
void Tarjan(int u){
pre[u] = low[u] = ++dfs_clock;
vis[u] = 1;
S.push(u);
for(int i=head[u]; i!=-1; i=node[i].next){
int v = node[i].v;
if(!pre[v]){
Tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(vis[v] && pre[v] < low[u])
low[u] = pre[v];
}
if(pre[u] == low[u]){
point++;
int x;
do{
x = S.top();
vis[x] = 0;
belong[x] = point;
S.pop();
}while(x != u);
}
return;
}
void solve(){
for(int i=1; i<=n; i++)
if(!pre[i]) Tarjan(i);
if(point == 1){
puts("0");
return;
}
for(int u=1; u<=n; u++){
for(int i=head[u]; i!=-1; i=node[i].next){
int v = node[i].v;
if(belong[u] != belong[v]){
out[belong[u]]++;
in[belong[v]]++;
}
}
}
int a = 0, b = 0;
for(int i=1; i<=point; i++){
if(!in[i]) a++;
if(!out[i]) b++;
}
printf("%d\n",max(a, b));
}
int main(){
int cas;
scanf("%d",&cas);
while(cas--){
scanf("%d%d",&n,&m);
init();
for(int i=0; i<m; i++){
int u, v;
scanf("%d%d",&u,&v);
addedge(u, v);
}
solve();
}
return 0;
}