StkOvflow

STACK OVERFLOW!

一言(ヒトコト)

AcWing1174. 受欢迎的牛

题目描述

每一头牛的愿望就是变成一头最受欢迎的牛。

现在有 \(N\) 头牛,编号从 \(1\)\(N\),给你 \(M\) 对整数 \((A,B)\),表示牛 \(A\) 认为牛 \(B\) 受欢迎。

这种关系是具有传递性的,如果 \(A\) 认为 \(B\) 受欢迎,\(B\) 认为 \(C\) 受欢迎,那么牛 \(A\) 也认为牛 \(C\) 受欢迎。

你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。

解题思路

\(\qquad\)简要题意:$$\LARGE 求一张图上有多少个点可以被所有其它点走到$$
\(\qquad\)我们首先从拓扑图DAG的角度考虑,在一张连通的拓扑图上,至多有一个点出度为\(0\),而这个点出度为\(0\)就肯定是在拓扑序的尾巴,也就是前面的所有点都可以走到这个点,那它就是我们要的被除自己之外的所有牛认为是受欢迎的。 并且,如果存在两个及以上的点出度为\(0\),代表图不连通,那对于每个点就至少有一个点走不到它,因此答案为\(0\)

\(\qquad\)关键是题目给出的图它不是拓扑图啊,但是我们可以通过Tarjan算法把每个强连通分量缩成一个点,并建立一张新图,这时候这张图就是拓扑图了。

对于\(Tarjan\)算法,引入了时间戳和追溯值

\[时间戳dfn[]表示的是在搜索树上的dfs序。 \]

\[追溯值low[]表示的是从当前节点 x 作为搜索树的根节点出发, \]

\[能够访问到的所有节点中,时间戳最小的值 \]

对于能够访问的节点,作出以下限制为:

\[\large 1.以 x 为根的搜索树的所有节点 \]

\[\large 2.通过一条非搜索树上的边,能够到达搜索树的所有节点 \]

当一个点的时间戳与追溯值相等,就代表它是所在的强连通分量中的最高点。
此时就代表我们可以顺藤摸瓜找出整个强连通分量了。

这题缩点之后就是简单题了

代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e4 + 10, M = 1e5 + 10;
int h[N], e[M], ne[M], idx;
int scc_cnt, in_stk[N], dfn[N], low[N];
int n, m, u, v, stmp, sz[N];
int stk[N], top, id[N], dout[N];

void add(int a, int b) 
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void tarjan(int u) 
{
    dfn[u] = low[u] = ++ stmp;
    stk[ ++ top] = u, in_stk[u] = true ;
    
    for (int i = h[u]; ~i; i = ne[i]) 
    {
        int j = e[i];
        if (!dfn[j]) 
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (in_stk[j]) low[u] = min(low[u], dfn[j]);
    }
    
    if (dfn[u] == low[u]) 
    {
        int y; ++ scc_cnt;
        do 
        {
            y = stk[top -- ];
            in_stk[y] = false ;
            id[y] = scc_cnt;
            sz[scc_cnt] ++ ;
        } while (y != u);
    }
}

int main() 
{
    scanf("%d%d", &n, &m);
    
    memset(h, -1, sizeof h);
    while (m -- ) 
    {
        scanf("%d%d", &u, &v);
        add(u, v);
    }
    
    for (int i = 1; i <= n; i ++ ) 
        if (!dfn[i]) tarjan(i);
        
    int zero(0), sum(0);
    for (int i = 1; i <= n; i ++ )
        for (int j = h[i]; ~j; j = ne[j]) 
        {
            int k = e[j], a = id[i], b = id[k];
            if (a != b) dout[a] ++ ;
        }
    
    for (int i = 1; i <= scc_cnt; i ++ ) 
    {
        if (!dout[i]) 
        {
            zero ++, sum += sz[i];
            if (zero > 1) 
            {
                sum = 0;
                break ;
            }
        }
    }
    
    printf("%d\n", sum);
    
    return 0;
}
posted @ 2023-01-05 10:08  StkOvflow  阅读(73)  评论(1)    收藏  举报