[SMOJ1419]受欢迎的奶牛

来源:互联网 发布:股市行情数据 编辑:程序博客网 时间:2024/05/15 09:10

等我搬这题笔记的时候,lgj 已经把 smoj 上对应的题封了,所以抱歉,只能搬原题(poj2186)过来,不便之处敬请原谅。

Description

Every cow’s dream is to become the most popular cow in the herd. In a herd of N (1N10,000) cows, you are given up to M (1M50,000) ordered pairs of the form (A,B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.

Input

Line 1: Two space-separated integers, N and M
Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.

Output

Line 1: A single integer that is the number of cows who are considered popular by every other cow.

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

Hint

Cow 3 is the only cow of high popularity.

Source

USACO 2003 Fall


很显然有若干个结点(奶牛),若干条边(认为受欢迎的关系),要找某种特定点的个数。这应该是一个图论的问题。

再来作进一步的分析。如果奶牛 A 认为奶牛 B 受欢迎,反之却不一定成立。显然这里的边应该是有向的。题目中有一个很重要的暗示,“受欢迎是可传递的”。这也就意味着最终要找的“被所有其他牛都喜欢的牛”必然满足:其他任何结点都要可以到达它。

什么情况下,其他结点可以到达自己呢?我们知道,在有向图的强连通分量中,任意两个结点之间互相可达。而将原图缩点后,就变成了一个 DAG 。既然无环,在新图中,出度为 0 的强连通分量,就可能是所求的答案。如下图的样例,缩点后 1 和 2 成为了一个强连通分量,出度为 1,3的出度为 0。因此 3 就是我们要求的点。

做法:Tarjan缩点,顺便记录新图中各个结点包含了原图中多少个结点,再判断新图中出度为0的新结点是否只有1个,如果是,则输出该新结点包含了原图中多少个结点。

证明:首先,在新图中,必然存在出度为 0 的结点,否则就会存在环,不是极大连通子图。(画几个例子就可以推导得出,有兴趣可以自行作严谨的证明)
其次,如果出度为 0 的新结点多于 1 个,由 DAG 的性质显然它们之间互相不可达,于是它们都不满足“其他任何结点都要可以到达它”的要求。
综上所述,原问题存在答案,当且仅当原图缩点后有 1 个出度为 0 的新结点,而该新结点所包含的原图结点个数就是所求答案;否则原问题的解为 0。

参考代码:

#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <stack>#include <vector>using namespace std;const int maxn = 1e4 + 100;const int maxm = 5e4 + 100;struct Edge {    int to, next;} edge[maxm];int cntEdge = 0;int n, m;int head[maxn];void addEdge(int u, int v) {    edge[++cntEdge].to = v;    edge[cntEdge].next = head[u];    head[u] = cntEdge;}int timeStamp = 0;int dfn[maxn], low[maxn];bool inStack[maxn];stack <int> st;int cntScc = 0;int belong[maxn];int num[maxn];void tarjan(int root) {    dfn[root] = low[root] = ++timeStamp;    inStack[root] = true;    st.push(root);    for (int i = head[root]; i; i = edge[i].next) {        int j = edge[i].to;        if (!dfn[j]) {            tarjan(j);            low[root] = min(low[root], low[j]);        } else if (inStack[j]) low[root] = min(low[root], dfn[j]);    }    if (dfn[root] == low[root]) {        ++cntScc;        int i;        do {            i = st.top(); st.pop();            inStack[i] = false;            belong[i] = cntScc;            ++num[cntScc];        }        while (i != root);    }}int outDegree[maxn]; //统计强连通分量的出度int main(void) {    freopen("1419.in", "r", stdin);    freopen("1419.out", "w", stdout);    scanf("%d%d", &n, &m);    for (int i = 0; i < m; i++) {        int a, b;        scanf("%d%d", &a, &b);        addEdge(a, b);    }    memset(dfn, 0, sizeof dfn);    memset(low, 0, sizeof low);    memset(inStack, false, sizeof inStack);    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 = edge[j].next)            if (belong[i] != belong[edge[j].to]) outDegree[belong[i]]++;    int cnt = 0; int ans;    for (int i = 1; i <= cntScc; i++)        if (!outDegree[i]) { cnt++; ans = num[i]; }    if (cnt == 1) printf("%d\n", ans); else puts("0");    return 0;}


0 0