题目
题目描述
一些学校连入一个电脑网络。那些学校已订立了协议:每个学校都会给其它的一些学校分发软件(称作“接受学校”)。注意即使 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
分析
一道模板题
对于任务A,就是缩点后看入度为零的点有多少个
对于任务B,我们可以发现,缩点后的图是一个有向无环图,
可以将整个图看成若干条链组成
每将一个入度为零的点连接上一个出度为零的点,就可以少一条链,而要消灭所有的链,答案就是链的条数也就是出度为0的点的个数与入度为0的点的个数中的较大值
(对于不是连通图的情况,可以将一个图的出度为0的点连上另一个图的入度为0的点,使他们变成一个图)
还有一点需要注意,要特判只有一个强联通分量的情况
代码
1 /************************* 2 User:Mandy.H.Y 3 Language:c++ 4 Problem:luogu2746 5 Algorithm: 6 Date:2019.8.28 7 *************************/ 8 9 #include<bits/stdc++.h> 10 #define Max(x,y) ((x) > (y) ? (x) : (y)) 11 #define Min(x,y) ((x) < (y) ? (x) : (y)) 12 13 using namespace std; 14 15 const int maxn = 105; 16 17 int first[maxn],size,n,tot,tp,cnt; 18 int dfn[maxn],low[maxn],vis[maxn],s[maxn],ind[maxn],outd[maxn]; 19 int father[maxn]; 20 21 struct Edge{ 22 int v,nt,u; 23 }edge[10005]; 24 25 template<class T>inline void read(T &x){ 26 x = 0;bool flag = 0;char ch = getchar(); 27 while(! isdigit(ch)) flag |= ch == '-',ch = getchar(); 28 while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar(); 29 if(flag) x = -x; 30 } 31 32 template<class T>void putch(const T x){ 33 if(x > 9) putch(x / 10); 34 putchar(x % 10 | 48); 35 } 36 37 template<class T>void put(const T x){ 38 if(x < 0) putchar('-'),putch(-x); 39 else putch(x); 40 } 41 42 void file(){ 43 freopen("2746.in","r",stdin); 44 freopen("2746.out","w",stdout); 45 } 46 47 void eadd(int u,int v){ 48 edge[++size].v = v; 49 edge[size].u = u; 50 edge[size].nt = first[u]; 51 first[u] = size; 52 } 53 54 void readdata(){ 55 read(n); 56 for(int i = 1;i <= n; ++ i){ 57 int v;read(v); 58 while(v){ 59 eadd(i,v); 60 read(v); 61 } 62 } 63 } 64 65 void tarjan(int u){ 66 dfn[u] = low[u] = ++tot; 67 vis[u] = 1; 68 s[tp++] = u; 69 70 for(int i = first[u];i;i = edge[i].nt){ 71 int v = edge[i].v; 72 if(!dfn[v]){ 73 tarjan(v); 74 low[u] = min(low[v],low[u]); 75 } else if(vis[v]) low[u] = min(low[u],dfn[v]); 76 } 77 78 if(dfn[u] == low[u]){ 79 int x;++cnt; 80 do{ 81 x = s[tp - 1]; 82 tp--; 83 father[x] = cnt; 84 vis[x] = 0; 85 }while(x != u); 86 } 87 } 88 89 void work(){ 90 for(int i = 1;i <= n; ++ i){ 91 if(!dfn[i]) tarjan(i); 92 } 93 94 for(int i = 1;i <= size; ++ i){ 95 int u = edge[i].u; 96 int v = edge[i].v; 97 if(father[u] != father[v]){ 98 ind[father[v]]++; 99 outd[father[u]]++; 100 } 101 } 102 103 int cntin = 0,cntout = 0; 104 105 for(int i = 1;i <= cnt; ++ i){ 106 if(!ind[i]) cntin++; 107 if(!outd[i]) cntout++; 108 } 109 if(cnt == 1){ 110 puts("1"); 111 puts("0"); 112 return; 113 } 114 put(cntin); 115 putchar(' '); 116 put(Max(cntin,cntout)); 117 } 118 119 int main(){ 120 // file(); 121 readdata(); 122 work(); 123 return 0; 124 }