[USACO5.3]校园网Network of Schools (强连通+缩点)

本文介绍如何使用Tarjan算法求解图中的所有强连通分量,并通过缩点操作解决特定的任务A和任务B。任务A的目标是计算新图中入度为0的点的数量;而任务B则要求得到入度为0的点数量和出度为0的点数量的最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:https://www.luogu.org/problemnew/show/P2746

分析

用Tarjan求出图中所有的强连通分量并进行缩点。新图中入度为0的点的数量为任务A的结果,入度为0的点的数量和出度为0的点的数量的最大值为任务B的结果。

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N=11000;
int n,a;
int cnt;
int u[N],v[N],f[N],next[N];
int nbn[N],ncnt,nbcnt;
int dfn[N],low[N],Index,is_sol[N];
int stack[N],sta_l,is_sta[N];
int q1,q2,in[N],out[N];
void tarjan(int nd){
    dfn[nd]=low[nd]=++Index;
    stack[++sta_l]=nd; 
    is_sta[nd]=is_sol[nd]=1;
    int ned=f[nd],nnd=v[ned];
    while (ned!=0){
        if (is_sta[nnd]) 
            low[nd]=min(low[nd],dfn[nnd]);
        else if (is_sol[nnd]==0){
            tarjan(nnd);
            low[nd]=min(low[nd],low[nnd]);
        }
        ned=next[ned]; nnd=v[ned];
    }
    if (dfn[nd]==low[nd]){
        nbcnt++;
        while (1){
            is_sta[stack[sta_l]]=0;
            nbn[stack[sta_l]]=nbcnt;
            sta_l--;
            if (stack[sta_l+1]==nd) break;
        }
    }
}
int main(){
    //freopen("Network_of_Schools.in","r",stdin);
    scanf("%d",&n);
    for (int i=1; i<=n; i++){
        while (1){
            scanf("%d",&a);
            if (a==0) break;
            u[++cnt]=i; v[cnt]=a;
            next[cnt]=f[i];
            f[i]=cnt;
        }
    }
    for (int i=1; i<=n; i++){
        if (dfn[i]==0) tarjan(i);
    }
    int nnn=nbn[1],ans=1;
    for (int i=2; i<=n; i++){
        if (nbn[i]==nnn) ans++;
    }//注意此处对原图为强连通图的特判
    if (ans!=n){
        for (int i=1; i<=n; i++){
            int ned=f[i];
            while (ned!=0){
                if (nbn[u[ned]]!=nbn[v[ned]])
                in[nbn[v[ned]]]=1;
                out[nbn[u[ned]]]=1;
                ned=next[ned];
            }
        }
        for (int i=1; i<=nbcnt; i++){
            if (in[i]==0) q1++;		
            if (out[i]==0) q2++;	
        } q2=max(q1,q2);
        printf("%d\n%d",q1,q2);
    }
    else printf("1\n0");
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值