题目背景

浙江省的几所\(OI\)强校的神犇发明了一种人工智能,可以\(AC\)任何题目,所以他们决定建立一个网络来共享这个软件。但是由于他们脑力劳动过多导致全身无力身体被\(♂\)\(♂\)空,他们来找你帮助他们。

题目描述

共有\(n\)所学校\((n \leq 10000)\)已知他们实现设计好的网络共\(m\)条线路,为了保证高速,网络是单向的。现在请你告诉他们至少选几所学校作为共享软件的母机母鸡,能使每所学校都可以用上。再告诉他们至少要添加几条线路能使任意一所学校作为母机母鸡都可以使别的学校使用上软件。

输入输出格式

输入格式:

第一行一个整数\(n\)

接下来\(n\)行每行有若干个整数,用空格空格隔开。

\(i-1\)行的非零整数\(x\),表示从\(i\)\(x\)有一条线路。以\(0\)作为结束标志。

输出格式:

第一行一个整数表示问题1的答案。

第二行回答问题2.

输入输出样例

输入样例#1:

5
2 0
4 0
5 0
1 0
0

输出样例#1:

2
2

说明

\(POJ\)原题。数据扩大了\(100\)倍。

\(边数 \leq 5000000 ≤ 5000000\)

思路:这道题是洛谷\(P2746\)的数据加强版,但是……还是没法卡掉\(tarjan\)做法啊,毕竟正解就是\(tarjan\),直接把洛谷\(P2746\)的代码粘过来然后把数组改大些就可以了……

代码:

#include<cstdio>
#include<iostream>
#include<stack>
#define maxn 10007
using namespace std;
int n,num,js,cnt,rd[maxn],cd[maxn],head[maxn],dfn[maxn],low[maxn],bel[maxn],size[maxn];
int x1,x2;
bool vis[maxn];
struct node {
  int v,nxt;
}e[5000007];
inline void ct(int u, int v) {
  e[++num].v=v;
  e[num].nxt=head[u];
  head[u]=num;
}
inline int maxx(int a, int b) {return a>=b?a:b;}
stack<int>q;
void tarjan(int u) {
  dfn[u]=low[u]=++cnt;
  q.push(u),vis[u]=1;
  for(int i=head[u];i;i=e[i].nxt) {
    int v=e[i].v;
    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 x=-1;js++;
    while(x!=u) {
      x=q.top(),q.pop();
      bel[x]=js,size[js]++;
      vis[x]=0;
    }
  }
}
int main() {
  scanf("%d",&n);
  for(int i=1,x;i<=n;++i) {
    while(scanf("%d",&x)==1) {
      if(!x) break;
      ct(i,x);
    }
  }
  for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
  if(js==1) {printf("1\n0\n");return 0;};
  for(int k=1;k<=n;++k) {
    for(int i=head[k];i;i=e[i].nxt) {
      int v=e[i].v;
      if(bel[k]!=bel[v]) ++cd[bel[k]],++rd[bel[v]];
    }
  }
  for(int i=1;i<=js;++i) {
    if(!cd[i]) ++x1;
    if(!rd[i]) ++x2;
  }
  printf("%d\n%d\n",x2,max(x1,x2));
  return 0;
}