题意:给出一个图,问最少加几条边使得该图成为强连通图
分析:先跑一遍 tarjan 算法求出强连图分量的个数,然后把这些分量缩成一个点,因为一个强连通图的每个点的入度和出度都不为零,所以统计这些点的入为0的数目和出度为0的数目,选择较大的输出即可
代码:
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define mod 1000000007
#define lowbit(x) (x&(-x))
#define mem(a) memset(a,0,sizeof(a))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
using namespace std;
typedef pair<int,int> pii;
const int maxn = 50000 + 7 , inf = 0x3f3f3f3f ;
int dfn[maxn],inStack[maxn],low[maxn],sccno[maxn],in[maxn],out[maxn];
int nxt[maxn],to[maxn],head[maxn];
int nEdge,n,m,tot,sccCnt;
stack<int>s;
void add(int u,int v){
to[nEdge] = v;
nxt[nEdge] = head[u];
head[u] = nEdge++;
}
void Init(){
tot = 0;
nEdge = 0;
sccCnt = 0;
while(!s.empty()) s.pop();
memset(head,-1,sizeof(head));
mem(dfn);mem(inStack);mem(low);mem(nxt);mem(to);mem(sccno);mem(in);mem(out);
}
void tarjan(int x){
dfn[x] = low[x] = ++tot;
s.push(x);
inStack[x] = 1;
for(int i = head[x]; ~i ; i = nxt[i]){
int v = to[i];
if(!dfn[v]){
tarjan(v);
low[x] = min(low[x],low[v]);
}
else if(inStack[v])
low[x] = min(low[x],dfn[v]);
}
if(dfn[x]==low[x]){
sccCnt++;
while(1){
int u = s.top();s.pop();
inStack[u] = 0;
sccno[u] = sccCnt;
if(x==u) break;
}
}
return;
}
int main(){
//FRER();
int T;
scanf("%d",&T);
while(T--){
Init();
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
if(sccCnt==1) {printf("0\n");continue;}
for(int i=1;i<=n;i++){//缩点
for(int j = head[i];~j;j=nxt[j]){
int v = to[j];
if(sccno[v]==sccno[i]) continue;
out[sccno[i]]++;in[sccno[v]]++;
}
}//统计入度出度
int inCnt=0,outCnt=0;
for(int i=1;i<=sccCnt;i++){
if(!in[i]) inCnt++;
if(!out[i]) outCnt++;
}
printf("%d\n",max(inCnt,outCnt));
}
}