poj2186
poj2186
题意
A -> B表示 A 认为 B是红人,且如果 B -> C,则 A -> C。问有几头牛被所有牛都认为是红人。
分析
如果A被所有牛认为是红人,那么A所在的强连通分量里的牛也被所有牛认为是红人。
至多只有一个强连通分量满足满足题目的条件。(否则这两个强连通分量合并,仍然是一个强连通分量)
按照Kosaraju算法 求强连通分量时,能够得到各个强连通分量拓扑排序后的顺序,而唯一能成为解的只有拓扑排序最后的连通分量,
(如果存在这样的强连通分量,那么一定会在第一个DFS中就找到(所有牛都认为它是红人),而由于rDFS逆序遍历,所以编号最大的就是那个强连通分量了。)
code
#include <iostream>
#include <cstdio>
#include <cstring>
#include<queue>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 1e4 + 10;
int n, m; // 点、边
int vis[MAXN];
int flag[MAXN]; // 所属强连通分量的拓扑序
vector<int> G[MAXN], rG[MAXN];
vector<int> vs; // 后序遍历顺序的顶点列表
void addedge(int x, int y)
{
G[x].push_back(y); // 正向图
rG[y].push_back(x); // 反向图
}
void dfs(int u)
{
vis[u] = 1;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if(!vis[v]) dfs(v);
}
vs.push_back(u);
}
void rdfs(int u, int k)
{
vis[u] = 1;
flag[u] = k;
for(int i = 0; i < rG[u].size(); i++)
{
int v = rG[u][i];
if(!vis[v]) rdfs(v, k);
}
}
int scc() // 强连通分量的个数
{
vs.clear();
memset(vis, 0, sizeof vis);
for(int i = 1; i <= n; i++) // [1...n]
if(!vis[i]) dfs(i);
memset(vis, 0, sizeof vis);
int k = 0;
for(int i = vs.size() - 1; i >= 0; i--)
if(!vis[vs[i]]) rdfs(vs[i], k++);
return k;
}
int solve()
{
int cnt = scc(), num = 0, u = 0;
for(int i = 1; i <= n; i++)
if(flag[i] == cnt - 1)
{
num++;
u = i;
}
memset(vis, 0, sizeof vis);
rdfs(u, 0);
for(int i = 1; i <= n; i++)
if(!vis[i]) num = 0;
return num;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
for(int i = 0; i <= n; i++)
{
G[i].clear();
rG[i].clear();
}
for(int i = 0; i < m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
addedge(x, y);
}
printf("%d\n", solve());
}
return 0;
}