传送门
问题:最少加几条有向边使原图形成一个强连通图?
tarjan缩点后形成一个DAG(有向无环图),统计入度为0的点个数和出度为0的点个数,最后答案为两者的最大值maxx,如果整个DAG为一个点,则答案为0(Special Judge),本蒟蒻只会感性证明,理性证明如果有哪位大佬会的话希望能在评论区指点一下。
感性证明:
在DAG中从出度为0的点加边连向入度为0的点,连maxx的边一定可以使原图强连通,少于maxx则无法成立(可以多画几个图检验一下,反正考试时不让证明只看答案,能够理解记忆就行)。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<stack>
using namespace std;
const int maxn=2e4+2,maxm=5e4+2;
int n,m;
int head[maxn],edge;
struct EDGE {
int u,v,nxt;
}e[maxm];
int dfn[maxn],low[maxn],ins[maxn],scc[maxn],snt,tim;
int in[maxn],out[maxn];
stack<int> S;
inline int read() {
int x=0;char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
inline void init() {
memset(head,-1,sizeof(head)),
memset(dfn,0,sizeof(dfn)),
memset(ins,0,sizeof(ins)),
memset(in,0,sizeof(in)),
memset(out,0,sizeof(out)),
edge=snt=tim=0;
}
inline void adde(int u,int v) {
e[edge].u=u,e[edge].v=v,e[edge].nxt=head[u],head[u]=edge++;
}
void dfs(int p) {
dfn[p]=low[p]=++tim,
ins[p]=1;
S.push(p);
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (!dfn[v]) {
dfs(v);
low[p]=min(low[p],low[v]);
}
else if (ins[v])
low[p]=min(low[p],dfn[v]);
}
if (low[p]==dfn[p]) {
++snt;
int t=-1;
while (t^p) {
t=S.top();
S.pop(),
ins[t]=0,
scc[t]=snt;
if (t==p) break;
}
}
}
inline int tarjan() {
for (int i=1;i<=n;++i)
if (!dfn[i]) dfs(i);
if (snt==1) return 0;
for (register int i=0;i<edge;++i) {
int u=scc[e[i].u],v=scc[e[i].v];
if (u!=v) ++in[v],++out[u];
}
int s1=0,s2=0;
for (int i=1;i<=snt;++i)
s1+=(in[i]==0),s2+=(out[i]==0);
return max(s1,s2);
}
int main() {
// freopen("hdu 2767.in","r",stdin);
int kase=read();
while (kase--) {
init();
n=read(),m=read();
for (register int i=1;i<=m;++i) {
int u=read(),v=read();
adde(u,v);
}
printf("%d\n",tarjan());
}
return 0;
}