洛谷 P2341 - 受欢迎的牛

题目描述

P2341 [USACO03FALL][HAOI2006]受欢迎的牛 G

解法:

首先,明确一下什么是明星奶牛:受欢迎的牛只可能是图中唯一的出度为0的强连通分量中的所有奶牛。

为什么?

强连通分量中,所有的结点都可以相互达到,也就是说强联通分量中的所有奶牛都相互喜欢。出度不为0的话,则强连通分量中的奶牛并不一定满足被所有奶牛喜欢的条件(当然,一个结点也算是某一个强联通分量,若该结点出度为0,也是一头明星奶牛)

若出现两个及两个以上出度为0的强连通分量,这两块是独立,换句话时候也就是第一分量的所有奶牛并不喜欢第二分量的任何一头奶牛,同理可推第二分量,所以这些独立的强连通分量中的奶牛都不是明星奶牛

#include <bits/stdc++.h>
using namespace std;

int n, m, a, b, t, cnt, cont, ans;
int v[100010], nex[100010]
int fst[10010], fn[10010], low[10010], scc[10010], ssize[10010], out[10010];
bool f[10010];
stack<int> s;

void tarjan(int k){
    low[k] = dfn[k] = ++t;
    f[k] = true;
    s.push(k);
    for(int i=fst[k];i!=-1;i=nex[i]){
        if(!dfn[v[i]]){
            tarjan(v[i]);
            low[k] = min(low[k],low[v[i]]);
        }
        else{
            if(f[v[i]])
                low[k] = min(low[k], dfn[v[i]]);
        }

    }
    if(low[k]==dfn[k]){
        f[k] = false;
        scc[k] = ++cnt; // cnt记录强连通分量个数,ssc表示k在第cnt个强联通分中
        ssize[cnt] = 1; // ssize记录第cnt个强联通分量的成员数
        while(s.top()!=k){
            scc[s.top()] = cnt;
            f[s.top()] = false;
            ssize[cnt]++;
            s.pop();
        }
        s.pop();
    }
    return;
}

int main()
{
    cin >> n >> m;
    memset(fst, -1, sizeof(fst));
    memset(low, 0x7f, sizeof(low));
    for(int i=1;i<=m;i++){
        scanf("%d%d", &a, &b);
        nex[i] = fst[a];
        v[i]= b;
        fst[a] = i;
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
            t = 0;
            tarjan(i);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=fst[i];j!=-1;j=nex[j]){
            if(scc[i]!=scc[v[j]]) // 如果i和其邻接点不再一个连通分量,则该连通分量有一个出度
                out[scc[i]]++;
        }
    }
    // 记录出度为0的强联通分量
    for(int i=1;i<=cnt;i++){
        if(out[i]==0){
            cont++;
            ans += ssize[i];
        }
    }
    if(cont==1)
        cout << ans;
    else
        cout << 0;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值