P2812 校园网络【[USACO]Network of Schools加强版】

题目背景

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

题目描述

共有n所学校(n<=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倍。

边数 ≤5000000

Solution:

  本题zyys(吐槽:数据加强?我先做的加强版,蒯了AC代码去普通版WA了,原因不说了)。

  思路:tarjan缩点。

  对于第1问,我们求出缩点后的各连通图中入度为0的点的个数就行了。

  对于第2问,等价于使得缩点后的各连通图中的每个点的出入度至少为1,不必考虑具体建图的过程(实际是出度为0连向入度为0,构成环),反正答案就是$max$(入度为0个数,出度为0个数)。

代码:

/*Code by 520 -- 8.21*/
#include<bits/stdc++.h>
#define il inline
#define ll long long 
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=20005,M=5000005;
struct node{
    int u,v;
}e[M];
int n,m,tot,dfn[N],low[N];
int to[M],net[M],h[N],cnt,la;
int stk[N],top,scc,bl[N],cd[N],rd[N];
int ans1,ans2;
bool ins[N];

int gi(){
    int a=0;char x=getchar();
    while(x<'0'||x>'9')x=getchar();
    while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+(x^48),x=getchar();
    return a;
}

il void add(int u,int v){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt;}

void tarjan(int u){
    dfn[u]=low[u]=++tot,stk[++top]=u,ins[u]=1;
    for(RE int i=h[u];i;i=net[i]){
        int v=to[i];
        if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
        else if(ins[v]) low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        scc++;
        while(stk[top+1]!=u)
            bl[stk[top]]=scc,ins[stk[top--]]=0;
    }
}

il void init(){
    n=gi();
    RE int v;
    For(i,1,n)
        while(1){
            v=gi();
            if(!v)break;
            add(i,v),e[++la].u=i,e[la].v=v;
        }
    For(i,1,n) if(!dfn[i]) tarjan(i);
    memset(h,0,sizeof(h)),cnt=0;
    if(scc==1)cout<<1<<'
'<<0,exit(0);
    For(i,1,la) if(bl[e[i].u]!=bl[e[i].v]) cd[bl[e[i].u]]++,rd[bl[e[i].v]]++;
    For(i,1,scc) {
        if(!rd[i]) ans1++;
        if(!cd[i]) ans2++;
    }
    printf("%d
%d",ans1,max(ans1,ans2));
}

int main(){
    init();
    return 0;
}
原文地址:https://www.cnblogs.com/five20/p/9515555.html