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;
    }
}