Islands(2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 F)
Problem Description
On the mysterious continent of Tamriel, there is a great empire founded by human.
To develope the trade, the East Empire Company is set up to transport goods from place to place.
Recently, the company wants to start their business in Solstheim, which is consists of N islands.
Luckily, there are already M sea routes.
All routes are one-way, and the i-th route can transport person and goods from island ui to vi.
Now, the company nominates you a particular job to plan some new routes to make sure that person and goods can be transported between any two islands.
Furthermore, because the neighboring regions are under attack by an increasing number of dragons, limited resources can be used to set up new routes.
So you should plan to build new routes as few as possible.
Input
The first line contains an integer T, indicating that there are T test cases.
For each test case, the first line includes two integers N (N≤10000) and M (M≤100000), as described above.
After that there are M lines. Each line contains two integers ui and vi.
Output
For each test case output one integer, represent the least number of routes required to new.
Sample Input
2
4 3
1 2
2 3
3 4
4 4
1 2
1 4
3 2
3 4Sample Output
1
2
题意:t 组数据,每组给出一个具有 n 个点 m 条边有向图,问要使得图变成强连通图最少要加几条边
思路:将图 tarjan 缩点后统计强连通分量与每个点的入度出度,如果强连通分量为 1 代表图已经是强连通的不需要加边,如果不为 1,输出入度、出度中最大的那个即为最少要加的边数
Source Program
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define EPS 1e-9
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
const int MOD = 1E9+7;
const int N = 200000+5;
const int dx[] = {1,-1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
int n,m;
vector<int> G[N];
stack<int> S;
int dfn[N],low[N];
bool vis[N];//标记数组
int sccno[N];//记录结点i属于哪个强连通分量
bool in[N],out[N];//记录入度、出度是否为0
int block_cnt;//时间戳
int sig;//记录强连通分量个数
void Tarjan(int x){
vis[x]=true;
dfn[x]=low[x]=++block_cnt;//每找到一个新点,纪录当前节点的时间戳
S.push(x);//当前结点入栈
for(int i=0;i<G[x].size();i++){//遍历整个栈
int y=G[x][i];//当前结点的下一结点
if(vis[y]==false){//若未被访问过
Tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(!sccno[y])//若已被访问过,且不属于任何一个连通分量
low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){//满足强连通分量要求
sig++;//记录强连通分量个数
while(true){//记录元素属于第几个强连通分量
int temp=S.top();
S.pop();
sccno[temp]=sig;
if(temp==x)
break;
}
}
}
void shrink(){//缩点
memset(in,false,sizeof(in));
memset(out,false,sizeof(out));
for(int i=1;i<=sig;i++){//对于所有的强连通分量,将其入度、出度均视为1
in[i]=true;
out[i]=true;
}
for(int x=0;x<n;x++){//枚举n个点
for(int i=0;i<G[x].size();i++){//对第x个点的每个后继节点
int y=G[x][i];
if(sccno[x]!=sccno[y]){//统计每个点出度、入度是否为0
out[sccno[x]]=false;
in[sccno[y]]=false;
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
G[i].clear();
while(m--){
int x,y;
scanf("%d%d",&x,&y);
x--;
y--;
G[x].push_back(y);
}
sig=0;
block_cnt=0;
memset(vis,false,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(sccno,0,sizeof(sccno));
//Tarjan求强连通分量
for(int i=0;i<n;i++)
if(vis[i]==false)
Tarjan(i);
shrink();//缩点
int a=0,b=0;
for(int i=1;i<=sig;i++){//统计入度、出度为0的点的个数
if(in[i])
a++;
if(out[i])
b++;
}
int res=max(a,b);
if(sig==1)//强连通分量为1时
res=0;
printf("%d\n",res);
}
}