• 「HAOI2006」受欢迎的牛


    每一头牛的愿望就是变成一头最受欢迎的牛。现在有 $N$ 头牛,给你 $M$ 对整数 $(A,B)$,表示牛 $A$ 认为牛 $B$ 受欢迎。这种关系是具有传递性的,如果 $A$ 认为 $B$ 受欢迎,$B$ 认为 $C$ 受欢迎,那么牛 $A$ 也认为牛 $C$ 受欢迎。你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。

    链接

    LOJ 10091

    Luogu 2341

    题解

    用 Tarjan 算法找出图中每一个强连通分量,缩点,然后转化为有向无环图。

    在一个强连通分量里的牛互相认为彼此是受欢迎的,由于关系是具有传递性的,只要其它强连通分量都有某头牛认为这个强连通分量的某头牛是受欢迎的,那么这个强连通分量的牛都是受欢迎的。

    对于已经完成缩点后的图:

    • 如果某个点存在出边,则出边指向的点不可能指向自身,因为这是无环图
    • 如果存在两个或两个以上的点没有出边,则这样的点均无法指向其它这样的点。

    所以,对于缩点后的图,只能有一个点被其它点指向或间接指向,否则输出 0。

    代码

    scc[u] 表示 $u$ 所在强连通分量的序号;sccSize[i] 表示序号为 $i$ 的强连通分量中,节点的数目;sccTot 表示强连通分量的个数。

    out[i] 表示序号为 $i$ 的强连通分量出边数目(出度)。

    #include <cstdio>
    #include <cstring>
    
    const int vSIZE = 100005;
    const int eSIZE = 400005;
    
    int out[vSIZE];
    int scc[vSIZE], sccSize[vSIZE], sccTot;
    int st[vSIZE], top;
    int low[vSIZE], dfn[vSIZE], time;
    int h[eSIZE], to[eSIZE], nxt[eSIZE], tot;
    
    int min(int x, int y) {
    	return x < y ? x : y;
    }
    
    void add(int x, int y) {
    	to[++tot] = y;
    	nxt[tot] = h[x];
    	h[x] = tot;
    }
    
    void tarjan(int x) {
    	low[x] = dfn[x] = ++time;
    	st[++top] = x;
    	for (int i = h[x]; i; i = nxt[i]) {
    		int y = to[i];
    		if (!dfn[y]) {
    			tarjan(y);
    			low[x] = min(low[x], low[y]);
    		} else if (!scc[y]) {
    			low[x] = min(low[x], dfn[y]);
    		}
    	}
    
    	if (low[x] == dfn[x]) {
    		scc[x] = ++sccTot;
    		sccSize[sccTot] ++;
    		while (st[top] != x) {
    			scc[st[top--]] = sccTot;
    			sccSize[sccTot] ++;
    		}
    		top--;
    	}
    }
    
    int main() {
    	int n, m, ans = 0;
    	scanf("%d %d", &n, &m);
    	for (int i = 1; i <= m; i++) {
    		int x, y;
    		scanf("%d %d", &x, &y);
    		add(x, y);
    	}
    	for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i);
    	for (int i = 1; i <= n; i++) {
    		for (int j = h[i]; j; j = nxt[j]) {
                // 两个点所属的强连通分量不同
    			if (scc[i] != scc[to[j]]) out[scc[i]]++;
    		}
    	}
    	for (int i = 1; i <= sccTot; i++) {
            // 如果没有出边
    		if (!out[i]) {
    			if (!ans) ans = sccSize[i]; // 这个强连通分量的牛都是受欢迎的
    			else ans = 0; // 已经存在点没有出边的情况
    		}
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    爬虫模块BeautifulSoup
    移动端开发概览【webview和touch事件】
    实战JS正则表达式
    相识HTML5 canvas
    运算符中的一些小技巧
    再看Ajax
    跨终端开发必备概念汇总
    谈谈工作,聊聊生活,想想以后
    页面元素坐标和偏移(clientX/pageX/screenX/layerX/offsetWidth/scrollWidth/clientWidth等)相关整理
    CSS3 Flexbox不迷路指南
  • 原文地址:https://www.cnblogs.com/lcfsih/p/14391403.html
Copyright © 2020-2023  润新知