题意:
给你一个有向图,让你添加最少的边,使得这个图任意两个点互相可达。
思路:
求强连通分量,缩点,求出缩点后的图的出度为0的点的个数c1,入度为0的点的个数c2.根据题意,只要添加线,使得不存在出度为0和入度0的点即可所以最少需要max(c1,c2);
注意特判强连通分量为1的情况。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4+7;
vector<int> edges[maxn];
int sccno[maxn],pre[maxn],lowlink[maxn];
int dfs_clock,scc_cnt;
int in[maxn],out[maxn];
stack<int> s;
void dfs(int u)
{
pre[u] = lowlink[u] = ++dfs_clock;
s.push(u);
for(int i = 0;i<edges[u].size();i++)
{
int v = edges[u][i];
if(!pre[v])
{
dfs(v);
lowlink[u] = min(lowlink[u],lowlink[v]);
}
else if(!sccno[v])
{
lowlink[u] = min(lowlink[u],pre[v]);
}
}
if(lowlink[u]==pre[u])
{
scc_cnt++;
while(1)
{
int x = s.top();
s.pop();
sccno[x] = scc_cnt;
if(x==u)
break;
}
}
}
void fin_scc(int n)
{
dfs_clock = 0;
scc_cnt = 0;
memset(sccno,0,sizeof(sccno));
memset(pre,0,sizeof(pre));
memset(lowlink,0,sizeof(lowlink));
for(int i = 1;i<=n;i++)
{
if(!pre[i])
dfs(i);
}
}
int slove(int n)
{
if(scc_cnt==1)
return 0;
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(int i = 1;i<=n;i++)
{
for(int j = 0;j<edges[i].size();j++)
{
int v = edges[i][j];
if(sccno[i]!=sccno[v])
{
in[sccno[v]]++;
out[sccno[i]]++;
}
}
}
int c1 = 0,c2 = 0;
for(int i = 1;i<=scc_cnt;i++)
{
if(in[i]==0)
{
c1++;
}
if(out[i]==0)
{
c2++;
}
}
return max(c1,c2);
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i<=n;i++)
{
edges[i].clear();
}
for(int i = 0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
edges[u].push_back(v);
}
fin_scc(n);
printf("%d\n",slove(n));
}
return 0;
}