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


Source


IOI 1996

第一个问题是问最少要几个点,才能使软件到达其他学校
第二个问题是问最少加多少条边,使得图变成强连通图,

对图求好强连通分量后,缩点,把每个强连通分量看成1个点,判断有多少个入度为0的,就是第一个问题的答案,判断有多少个出度为0的,然后在两者中去大者,就是第二个问题的答案。具体证明请参考kuangbin大神的博客: 点击打开链接

#include<stdio.h>
#include<string.h>

const int maxn=100050;
int DFN[maxn];//记录每个点被访问到的时间
int low[maxn];//记录点可以直接或间接到达的最早被访问到的点(也就是那个强连通分量的
int stack[maxn];
int sccnum[maxn];//标记每个点属于第几个强连通分量
int in[maxn],out[maxn];//出度和入度
bool instack[maxn];
int num[maxn];
int sccNum;//强连通分量的数目
int top;
int index;
int n;
struct node
{
    int to;
    int next;
}edge[maxn];
int head[maxn];
int tot;

int max(int a,int b)
{
	return a>b?a:b;
}

void addedge(int from,int to)
{
    edge[tot].to=to;
    edge[tot].next=head[from];
    head[from]=tot++;
}

void tarjan(int i)
{
    DFN[i]=low[i]=++index;//刚刚搜到这个点,DFN和low都赋值为被访问到的时间
    stack[top++]=i;//入栈
    instack[i]=1;
    for (int j=head[i];j!=-1;j=edge[j].next)
    {
        if (!DFN[edge[j].to])//如果没有被访问过
        {
            tarjan(edge[j].to);
            //这个时候low可能要修改,值为i或者i的子树可以到达的最早被访问到的点的时
            if (low[i]>low[edge[j].to])
                low[i]=low[edge[j].to];
        }
        else if (instack[edge[j].to])//已经在栈
        {
            if (low[i]>DFN[edge[j].to])
                low[i]=DFN[edge[j].to];
        }
    }
    if (DFN[i]==low[i])//找到根
    {
    	sccNum++;
    	int v;
        do
        {
            v=stack[--top];
            sccnum[v]=sccNum;
            num[sccNum]++;
            instack[v]=0;//标记出栈
        }while(v!=i);
    }
}

void solve()
{
    memset(DFN,0,sizeof(DFN));
    memset(instack,0,sizeof(instack));
    memset(num,0,sizeof(num));
    index=0;
    sccNum=0;
    top=0;
    for (int i=1;i<=n;i++)
        if (!DFN[i])
            tarjan(i);
}

int main()
{
    while(~scanf("%d",&n))
    {
    	memset(head,-1,sizeof(head));
    	tot=0;
    	for(int i=1;i<=n;i++)
    	{
    		int t;
	    	while(scanf("%d",&t) && t)
	    	{
	    		addedge(i,t);
	    	}
	    }
	    solve();
	    if(sccNum==1)
	      printf("1\n0\n");
        else
        {
        	memset(in,0,sizeof(in));
        	memset(out,0,sizeof(out));
        	for(int i=1;i<=n;i++)
        	  for(int j=head[i];j!=-1;j=edge[j].next)
        	  {
        	  		if(sccnum[i]!=sccnum[edge[j].to])
        	  		{
		  	        	in[sccnum[edge[j].to]]++;
		  	        	out[sccnum[i]]++;
		  	        }
  	          }
            int a=0,b=0;
            for(int i=1;i<=sccNum;i++)
            {
            	if(in[i]==0)
            	  a++;
          	    if(out[i]==0)
          	      b++;
            }
            printf("%d\n",a);
            printf("%d\n",max(a,b));
        }
    }
    return 0;
}




阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6