​VJ传送门​

开始练kuangbin的连通图专题了

对这题就是直接上 t a r j a n tarjan tarjan缩点

因为同一个联通分量是可以互相到达的,缩成为一个点

第一问

那么只需要给每个没有入边的点出发,就能到达所有点了.

第二问

取没有入边的点和没有出边的点的 m a x max max即可

因为如果所有点都有入度和出度就会满足条件

每条边只提供一个入度和一个出度

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <cstdio>
using namespace std;
const int maxn = 2e5+10;
struct edge{
int u,to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){ d[++cnt] = (edge){u,v,head[u]},head[u] = cnt; }
int dfn[maxn],stac[maxn],num,top,vis[maxn],scc,sd[maxn],low[maxn];
int in[maxn],out[maxn],n;
vector<int>vec[maxn];
void tarjan(int u)
{
dfn[u] = low[u] = ++num;
vis[u] = 1, stac[++top] = u;
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( !dfn[v] )
{
tarjan(v);
low[u] = min( low[u],low[v] );
}
else if( vis[v] )
low[u] = min( low[u],dfn[v] );
}
if( dfn[u] == low[u] )//发现强连通
{
int temp; scc++;
while( temp = stac[top--] )
{
sd[temp] = scc;
vis[temp] = 0;
if( temp==u ) break;
}
}
}
int main()
{
while( cin >> n )
{
for(int i=1;i<=n;i++)
{
int x;
while( scanf("%d",&x) && x ) add(i,x);
}
for(int i=1;i<=n;i++)
if( !dfn[i] ) tarjan(i);
if( scc == 1 )
{
cout << 1 << endl << 0 << endl;
continue;
}
for(int i=2;i<=cnt;i++)//遍历每一条边
{
int u = d[i].u, v = d[i].to;
if( sd[u]!=sd[v] )
in[sd[u]]++, out[sd[v]]++;
}
int chu=0, ru = 0;
for(int i=1;i<=scc;i++)
{
if( in[i]==0 ) ru++;
if( out[i]==0 ) chu++;
}
cout << chu << "\n" << max( ru,chu ) << endl;
scc = top = 0, cnt = 1;
for(int i=1;i<=n;i++)
head[i] = low[i] = dfn[i] = vis[i] = in[i] = out[i] = 0;
}
}