[USACO03FALL]受欢迎的牛

明星奶牛数量求解

题目传送门

题面

题目背景

本题测试数据已修复。

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 A A A 喜欢 B B B B B B 喜欢 C C C,那么 A A A 也喜欢 C C C。牛栏里共有 N N N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

输入格式

第一行:两个用空格分开的整数: N N N M M M

接下来 M M M 行:每行两个用空格分开的整数: A A A B B B,表示 A A A 喜欢 B B B

输出格式

一行单独一个整数,表示明星奶牛的数量。

输入输出样例 #1

输入 #1

3 3
1 2
2 1
2 3

输出 #1

1

说明/提示

只有 3 3 3 号奶牛可以做明星。

【数据范围】

对于 10 % 10\% 10% 的数据, N ≤ 20 N\le20 N20 M ≤ 50 M\le50 M50

对于 30 % 30\% 30% 的数据, N ≤ 1 0 3 N\le10^3 N103 M ≤ 2 × 1 0 4 M\le2\times 10^4 M2×104

对于 70 % 70\% 70% 的数据, N ≤ 5 × 1 0 3 N\le5\times 10^3 N5×103 M ≤ 5 × 1 0 4 M\le5\times 10^4 M5×104

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 4 1\le N\le10^4 1N104 1 ≤ M ≤ 5 × 1 0 4 1\le M\le5\times 10^4 1M5×104

思路

用 TARJAN 缩点之后,不存在相互爱慕的关系(无环),所以出度唯一为 0 0 0 的强连通分量的所有奶牛是明星。如果有多个强连通分量出度为 0 0 0 则说明没有明星(有其他强连通分量出度为 0 0 0 等于有奶牛没有爱慕我,等于我不是明星)。

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int n,m;
int dfn[10004],low[10004],visited[10004],nowdfn = 1;
int scc[10004],siz[10004],scs,dag_in[10004];
vector <int> pict[10004];
vector <int> dag[10004];
stack <int> vis;
void tarjan(int now)
{
    dfn[now] = low[now] = nowdfn++;
    vis.push(now);
    visited[now] = 1;
    for(auto i : pict[now])
    {
        if(!dfn[i])
        {
            tarjan(i);
            low[now] = min(low[now],low[i]);
        }
        else if(visited[i]) low[now] = min(low[now],dfn[i]);
    }
    if(dfn[now] == low[now])
    {
        scs++;
        while(vis.top() != now)
        {
            siz[scs]++;
            scc[vis.top()] = scs;
            visited[vis.top()] = 0;
            vis.pop();
        }
        siz[scs]++;
        scc[vis.top()] = scs;
        visited[vis.top()] = 0;
        vis.pop();
    }
    return;
}
int main()
{
    cin >> n >> m;
    for(int i = 1;i <= m;i++)
    {
        int a,b;
        cin >> a >> b;
        pict[a].push_back(b);
    }
    for(int i = 1;i <= n;i++) if(!dfn[i]) tarjan(i);
    for(int i = 1;i <= n;i++)
    {
        for(auto j : pict[i])
        {
            if(scc[j] != scc[i])
            {
                dag[scc[i]].push_back(scc[j]);
                dag_in[scc[i]]++;
            }
        }
    }
    int flag = 0;
    for(int i = 1;i <= scs;i++)
    {
        if(!dag_in[i] && flag)
        {
            flag = 0;
            break;
        }
        if(!dag_in[i]) flag = i;
    }
    if(flag) cout << siz[flag];
    else cout << 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值