@[toc]

Description

A number of schools are connected to a computer network. Agreements
have been developed among those schools: each school maintains a list
of schools to which it distributes software (the “receiving schools”).
Note that if B is in the distribution list of school A, then A does
not necessarily appear in the list of school B You are to write a
program that computes the minimal number of schools that must receive
a copy of the new software in order for the software to reach all
schools in the network according to the agreement (Subtask A). As a
further task, we want to ensure that by sending the copy of new
software to an arbitrary school, this software will reach all schools
in the network. To achieve this goal we may have to extend the lists
of receivers by new members. Compute the minimal number of extensions
that have to be made so that whatever school we send the new software
to, it will reach all other schools (Subtask B). One extension means
introducing one new member into the list of receivers of one school.

Input

The first line contains an integer N: the number of schools in the
network (2 <= N <= 100). The schools are identified by the first N
positive integers. Each of the next N lines describes a list of
receivers. The line i+1 contains the identifiers of the receivers of
school i. Each list ends with a 0. An empty list contains a 0 alone in
the line.

Output

Your program should write two lines to the standard output. The first
line should contain one positive integer: the solution of subtask A.
The second line should contain the solution of subtask B.

Sample Input

5
2 4 3 0
4 5 0
0
0
1 0

Sample Output

1
2

题意:

给你一些点的连接关系(有向边),具有传递性
然后有两个任务
任务A:
至少要选择多少个点,通过边进行传递,可以经过所有边
任务B:
要使所有点都可以彼此到达,最少要添加多少有向边

题解:

并查集应该可以做吧(但我并没有用)
我们用tarjan来做
我们先来分析和整合题意:
一个强连通分块里面是可以相互到达的,我们可以当做一个点来处理
任务A:
我们可以进行tarjan缩点,如果一个点入度为0,也就是没有其他点指向他,我们就必须选择他,否则这样的点就不会被遍历,那么这个点就是满足要求的
任务B:
其实就是问最少添加几个边可以使得整个图变成强连通图,
如果一个图是强连通图,里面所有点可以互达,就说明每个点入度出度均大于0.
那么缩点后,我们就统计入度为0的A类点数与出度为0的B类点数,只要从B类点生成一条指向A类点的有向线段即可,所以求这两类点的较大值
总结就是:
Tarjan+强连通分量+缩点

代码:

#include<bits/stdc++.h>
using namespace std;
int n,head[10005],cnt=0,col=0,sum;
int top=1,sta[10005],color[10005],tim[10005],low[10005],ins[10005];//color记录染色后点的颜色。
int in[10005],out[10005];//in和out分别是入度和出度。
struct net
{
     int to,next;
}e[1000000];
void add(int x,int y)
{
    cnt++;
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt;
}
void tarjian(int x)//常规缩点
{
    sum++;
    tim[x]=low[x]=sum;
    sta[top]=x;
    top++;
    ins[x]=1;
    for(int w=head[x];w!=0;w=e[w].next)
    {
         if(ins[e[w].to]==0)
          {
                tarjian(e[w].to);
                low[x]=min(low[x],low[e[w].to]);
          }
         else if(ins[e[w].to]==1)
                low[x]=min(low[x],tim[e[w].to]);
    }
    if(tim[x]==low[x])
    {
        col++;
        do
        {
            top--;
            color[sta[top]]=col;
            ins[sta[top]]=-1;
        }while(sta[top]!=x);
    }
    return ;
}
int hym[1000000][3];
int main()
{
     int i,k=0;
     cin>>n;
     for(i=1;i<=n;i++)
     {
       int x;
       cin>>x;
       while(x!=0)
       {
              k++;
              add(i,x);
       hym[k][1]=i;
           hym[k][2]=x;//记下边,方便之后统计入度和出度。
           scanf("%d",&x);
       }
     }
     for(i=1;i<=n;i++)
         if(!ins[i]) tarjian(i);
     for(i=1;i<=k;i++)
         if(color[hym[i][1]]!=color[hym[i][2]])//同种颜色不需要统计,不同颜色更改出度和入度。
           {
                 out[color[hym[i][1]]]++;
                 in[color[hym[i][2]]]++;
           }
     int ans1=0,ans2=0;
     for(i=1;i<=col;i++)
     {
         //cout<<in[i]<<' '<<out[i]<<endl;
         if(in[i]==0)  ans1++;
         if(out[i]==0) ans2++;
     }
     if(col==1) cout<<1<<endl<<0;
     else
        cout<<ans1<<endl<<max(ans1,ans2);
     return 0;
}