P2746 [USACO5.3]校园网Network of Schools(Tarjan)

P2746 [USACO5.3]校园网Network of Schools

题目描述

一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 B 在 A 学校的分发列表中, A 也不一定在 B 学校的列表中。

你要写一个程序计算,根据协议,为了让网络中所有的学校都用上新软件,必须接受新软件副本的最少学校数目(子任务 A)。更进一步,我们想要确定通过给任意一个学校发送新软件,这个软件就会分发到网络中的所有学校。为了完成这个任务,我们可能必须扩展接收学校列表,使其加入新成员。计算最少需要增加几个扩展,使得不论我们给哪个学校发送新软件,它都会到达其余所有的学校(子任务 B)。一个扩展就是在一个学校的接收学校列表中引入一个新成员。

输入输出格式

输入格式:

 

输入文件的第一行包括一个整数 N:网络中的学校数目(2 <= N <= 100)。学校用前 N 个正整数标识。

接下来 N 行中每行都表示一个接收学校列表(分发列表)。第 i+1 行包括学校 i 的接收学校的标识符。每个列表用 0 结束。空列表只用一个 0 表示。

 

输出格式:

 

你的程序应该在输出文件中输出两行。

第一行应该包括一个正整数:子任务 A 的解。

第二行应该包括子任务 B 的解。

 

输入输出样例

输入样例#1: 复制
5
2 4 3 0
4 5 0
0
0
1 0
输出样例#1: 复制
1
2

说明

题目翻译来自NOCOW。

USACO Training Section 5.3

/*
1.统计入度0的点
2.计算入度和出度为0点数量的较大值
注意特判1个强连通分量的情况 
*/
#include<bits/stdc++.h>

#define N 1007

using namespace std;
int n,m,ans,cnt,top,num;
int head[N],sta[N],in_st[N],can[N][N];
int dfn[N],low[N],scc[N],in[N],out[N];
struct edge{
    int u,v,net;
}e[N<<1],E[N<<1];

inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

inline void add(int u,int v)
{
    e[++cnt].v=v;e[cnt].net=head[u];head[u]=cnt;
}

void Tarjan(int u)
{
    low[u]=dfn[u]=++cnt;sta[++top]=u;in_st[u]=1;
    for(int i=head[u];i;i=e[i].net)
    {
        int v=e[i].v;
        if(!dfn[v]) 
          Tarjan(v),low[u]=min(low[u],low[v]);
        else if(in_st[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        while(u!=sta[top])
        {
            in_st[sta[top]]=0; scc[sta[top]]=num;top--;
        }
        in_st[sta[top]]=0; scc[sta[top]]=num;top--;
        num++;
    }
}

int main()
{
   
    n=read();int x;
    for(int i=1;i<=n;i++)
        while(scanf("%d",&x)!=0)
      {
            if(x==0) break;
          add(i,x);    
      } 
    cnt=0;top=0;num=1;
      for(int i=1;i<=n;i++)
        if(!dfn[i]) Tarjan(i);
      cnt=0;num--;
      for(int i=1;i<=n;i++) for(int j=head[i];j;j=e[j].net)
      {
          int v=e[j].v;
          if(scc[i]!=scc[v] && !can[scc[i]][scc[v]]) 
          {
              can[scc[i]][scc[v]]=1;
            in[scc[v]]++;out[scc[i]]++;
        } 
    }
    cnt=0;
    for(int i=1;i<=num;i++) if(in[i]==0) cnt++;
    printf("%d\n",cnt);
    top=0;
    for(int i=1;i<=num;i++) if(out[i]==0) top++;
    if(num!=1)printf("%d\n",max(cnt,top));
    else printf("0\n");
    return 0;
}

 

posted @ 2018-10-17 17:24  安月冷  阅读(167)  评论(0编辑  收藏  举报