题目
题意:
给定一张有向图,可以在上面建边,输出构建尽可能少的边使得从给定的起点开始可以到达图上的任意一点。
1
≤
n
≤
5000
,
0
≤
m
≤
5000
,
1
≤
s
≤
n
1≤n≤5000,0≤m≤5000,1≤s≤n
1≤n≤5000,0≤m≤5000,1≤s≤n
分析:
我们思考该如何建边,显然应该向一个任何点都无法到达的点建一条边,即这个点的入度为0。这样我们就可以利用dfs来更新可达的点,当某个点入度为0且没被更新时,建一条边到这个点,继续更新未达的点。由于有向图存在强连通分量,所以还需要tarjan+缩点处理一下。
#include <iostream>
#include <vector>
#include <stack>
#include <cstring>
using namespace std;
vector<int> g[5005],g_new[5005];
int vis[5005],in[5005];
int dfn[5005],low[5005];
int scc[5005],c = 1;
stack<int> s;
int cnt = 0;
void dfs(int x)
{
vis[x] = 1;
for (int i = 0; i < g_new[x].size(); i++)
{
int t = g_new[x][i];
if( vis[t] ) continue;
dfs(t);
}
}
void tarjan(int x)
{
dfn[x] = low[x] = ++cnt;
s.push(x);
vis[x] = 1;
for (int i = 0; i < g[x].size(); i++)
{
int t = g[x][i];
if( !dfn[t] )
{
tarjan(t);
low[x] = min(low[x],low[t]);
}else if( vis[t] )
{
low[x] = min(low[x],low[t]);
}
}
if( dfn[x] == low[x] )
{
vis[x] = 0;
scc[x] = c;
while( s.top() != x )
{
vis[s.top()] = 0;
scc[s.top()] = c;
s.pop();
}
c ++;
s.pop();
}
}
void compress(int n)
{
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < g[i].size(); j++)
{
int t = g[i][j];
if( scc[i] != scc[t] )
{
g_new[scc[i]].push_back(scc[t]);
in[scc[t]] ++;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m,begin;
cin >> n >> m >> begin;
for (int i = 1; i <= m; i++)
{
int x,y;
cin >> x >> y;
g[x].push_back(y);
}
for (int i = 1; i <= n; i++)
{
if( !dfn[i] ) tarjan(i);
}
compress(n);
memset(vis,0,sizeof(vis));
int ans = 0;
dfs(scc[begin]);
for (int i = 1; i < c; i++)
{
if( in[i] == 0 && vis[i] == 0 )
{
ans ++;
dfs(i);
}
}
cout << ans << '\n';
return 0;
}