题解:
问题等价于:
1
1
、至少要选几个点,才能使得从这些点出发能到达所有点。
、至少要加几条边,才能使得这个图成为一个强连通分量。
Tarjan
T
a
r
j
a
n
缩点后,问题
1
1
的答案即为入度为的点的个数。
对于问题
2
2
,需要为所有入度为的点增加一条入边,为所有出度为
0
0
的点增加一条出边。
假设入度为的点有
n
n
个,出度为的点有
m
m
个,那么答案即为。
注意原图就是一个强连通分量的特殊情况。
Code:
C
o
d
e
:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define N 10005
using namespace std;
int dfn[N],low[N],deep,a[N],top,flag[N],color[N],sum,cnt[N],out[N],in[N];
vector<int> edge[N];
void tarjan(int u)
{
low[u]=dfn[u]=++deep;
a[++top]=u;flag[u]=true;
for(int i=0;i<edge[u].size();i++)
{
int v=edge[u][i];
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}else if(flag[v])
low[u]=min(low[u],low[v]);
}
if(low[u]==dfn[u])
{
color[u]=++sum;
flag[u]=0;
while(a[top]!=u)
{
flag[a[top]]=false;
color[a[top--]]=sum;
}
top--;
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
while(x)
{
edge[i].push_back(x);
scanf("%d",&x);
}
}
memset(dfn,0,sizeof(dfn));
memset(flag,false,sizeof(flag));
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int u=1;u<=n;u++)
{
for(int i=0;i<edge[u].size();i++)
{
int v=edge[u][i];
if(color[u]!=color[v])
{
in[color[v]]++;
out[color[u]]++;
}
}
cnt[color[u]]++;
}
int tot=0,ans=0;
for(int i=1;i<=sum;i++)
if(in[i]==0)tot++;else
if(out[i]==0)ans++;
if(sum==1)
{
puts("1");
puts("0");
}else
{
printf("%d\n",tot);
printf("%d\n",max(ans,tot));
}
return 0;
}