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

题目描述

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

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

输入格式

输入文件的第一行包括一个正整数 NN,表示网络中的学校数目。学校用前 NN 个正整数标识。

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

输出格式

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

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

第二行应该包括一个非负整数,表示子任务 B 的解。


题解:
Tarjan缩点,那么子任务A:必须接受的学校数目就是入度为0的点的数目,考虑子任务B:至少加边让图变成一个环,我们这里只考虑入度为0的点和出度为0的点,因为其他点都是可以互相到达的,要求最少的边数,也就是将出度为0的点与入度为0的点连边,所以答案就是两种点的数目取max即可


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define LL long long
const int MAXN = 100+50;
const int MAXM = 10000+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
int dfn[MAXN],low[MAXN],head[MAXN],to[MAXM],nxt[MAXM];
int n,k,tot,cnt,top,instack[MAXN],st[MAXN],bel[MAXN];
int in[MAXN],out[MAXN];
inline void add(int u,int v){ to[++tot]=v; nxt[tot]=head[u]; head[u]=tot; }
void tarjan(int u){
    dfn[u]=low[u]=++cnt; st[++top]=u; instack[u]=1;
    for(int i=head[u];i;i=nxt[i]){
        if(!dfn[to[i]]) tarjan(to[i]),low[u]=min(low[u],low[to[i]]);
        else if(instack[to[i]]) low[u]=min(low[u],dfn[to[i]]);
    }
    if(dfn[u]==low[u]){
        ++k; int v;
        do{
            v=st[top--]; instack[v]=0; bel[v]=k;
        }while(u!=v);
    }
}
signed main(){
#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
    scanf("%d",&n); int res=0,ans=0;
    for(int i=1,x;i<=n;i++) while(scanf("%d",&x) && x) add(i,x);
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++)
        for(int j=head[i];j;j=nxt[j])
            if(bel[i]!=bel[to[j]])
                in[bel[to[j]]]++,out[bel[i]]++;
    for(int i=1;i<=k;i++) if(!in[i]) res++;
    for(int i=1;i<=k;i++) if(!out[i]) ans++;
    if(k==1) cout<<1<<'\n'<<0<<'\n';
    else cout<<res<<'\n'<<max(ans,res)<<'\n';
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值