把一个图变成强联通,首先进行缩点,把原来的图变成很多的DAG,然后把所有出度为零的点和入度为零的点一一连起来,就得到了一个最少加边的强联通分量
#include<stdio.h>
#include<vector>
#include<algorithm>
#include<stack>
#include<string.h>
using namespace std;
#define MS(x) memset(x,0,sizeof(x))
stack<int> S;
vector<int> G[23000];
vector<int> C[23000];
bool used[25000];
int dfn[25000];
int low[25000];
int time=0;
int nv,ne;
int cmp[25000];
int ncmp;
bool instack[25000];
int in[25000];
int out[25000];
int init()
{
while(!S.empty()) S.pop();
MS(used);
MS(low);
for(int i=0;i<25000;i++)
dfn[i]=-1;
time=0;
ncmp=0;
MS(cmp);
MS(G);
MS(C);
MS(instack);
MS(in);
MS(out);
}
void tarjan(int n)
{
dfn[n]=low[n]=time++;
S.push(n);
instack[n]=1;
for(int i=0;i<G[n].size();i++)
{
int v=G[n][i];
if(dfn[v]==-1)
{
tarjan(v);
low[n]=min(low[n],low[v]);
}
else if(instack[v])
{
low[n]=min(low[n],dfn[v]);
}
}
if(dfn[n]==low[n])
{
int v;
do
{
v=S.top();
S.pop();
instack[v]=false;
cmp[v]=ncmp;
}while(v!=n);
ncmp++;
}
}
int main()
{
int cas;
scanf("%d",&cas);
for(int T=1;T<=cas;T++)
{
init();
scanf("%d%d",&nv,&ne);
for(int i=0;i<ne;i++)
{
int f,t;
scanf("%d%d",&f,&t);
f--;
t--;
G[f].push_back(t);
}
for(int i=0;i<nv;i++)
if(dfn[i]==-1)
tarjan(i);
printf("Case %d: ",T);
//printf("ncmp:%d\n",ncmp);
if(ncmp==1)
{
puts("0");
continue;
}
for(int i=0;i<nv;i++)
{
for(int j=0;j<G[i].size();j++)
{
int v=G[i][j];
int cn=cmp[i];
int cv=cmp[v];
if(cn!=cv)
C[cn].push_back(cv);
}
}
int ans=0;
for(int i=0;i<ncmp;i++)
{
out[i]=C[i].size();
for(int j=0;j<C[i].size();j++)
{
int v=C[i][j];
in[v]++;
}
}
int in0=0;
int out0=0;
for(int i=0;i<ncmp;i++)
{
if(in[i]==0)
in0++;
if(out[i]==0)
out0++;
}
printf("%d\n",max(in0,out0));
}
return 0;
}