《P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G》
题目背景
本题测试数据已修复。
题目描述
每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 A 喜欢 B,B 喜欢 C,那么 A 也喜欢 C。牛栏里共有 N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。
输入格式
第一行:两个用空格分开的整数:N 和 M。
接下来 M 行:每行两个用空格分开的整数:A 和 B,表示 A 喜欢 B。
输出格式
一行单独一个整数,表示明星奶牛的数量。
输入输出样例
输入 #1复制
3 3 1 2 2 1 2 3
输出 #1复制
1
说明/提示
只有 3 号奶牛可以做明星。
【数据范围】
对于 10% 的数据,N≤20,M≤50。
对于 30% 的数据,N≤103,M≤2×104。
对于 70% 的数据,N≤5×103,M≤5×104。
对于 100% 的数据,1≤N≤104,1≤M≤5×104。
代码实现:
//Author:502BadGTway
#include<bits/stdc++.h>
using namespace std;
// 图的节点数和边数
int nodeCount, edgeCount;
// 邻接表相关:头指针、边指向的节点、下一条边
int head[10010], edgeTo[100010], nextEdge[100010];
// 输入的边的两个端点
int fromNode, toNode;
// 深度优先搜索序号和能回溯到的最小序号
int dfsNum[10010], lowNum[10010];
// 强连通分量相关:节点所属分量、序号计数器、分量大小、最小节点、出度
int component[10010], seqCounter, componentCount, componentSize[10010];
int outDegree[10010];
// 记录出度为0的分量数量和结果
int zeroOutComponentCount, result;
// 标记节点是否在栈中
bool inStack[10010];
// 存储当前路径的栈
stack<int> nodeStack;
// Tarjan算法求强连通分量
void tarjan(int currentNode)
{
// 初始化当前节点的序号和最小回溯序号
lowNum[currentNode] = dfsNum[currentNode] = ++seqCounter;
// 将节点入栈并标记
inStack[currentNode] = true;
nodeStack.push(currentNode);
// 遍历所有相邻节点
for(int i = head[currentNode]; i != -1; i = nextEdge[i])
{
int adjacentNode = edgeTo[i];
// 如果相邻节点未访问过
if(!dfsNum[adjacentNode])
{
tarjan(adjacentNode);
// 更新当前节点的最小回溯序号
lowNum[currentNode] = min(lowNum[currentNode], lowNum[adjacentNode]);
}
// 如果相邻节点已在栈中
else if(inStack[adjacentNode])
{
lowNum[currentNode] = min(lowNum[currentNode], dfsNum[adjacentNode]);
}
}
// 如果当前节点是强连通分量的根节点
if(lowNum[currentNode] == dfsNum[currentNode])
{
inStack[currentNode] = false;
component[currentNode] = ++componentCount;
componentSize[componentCount] = 1;
// 弹出栈中所有属于当前强连通分量的节点
while(nodeStack.top() != currentNode)
{
component[nodeStack.top()] = componentCount;
inStack[nodeStack.top()] = false;
componentSize[componentCount]++;
nodeStack.pop();
}
nodeStack.pop();
}
return ;
}
int main()
{
cin >> nodeCount >> edgeCount;
// 初始化邻接表头指针为-1(表示无后续边)
memset(head, -1, sizeof(head));
// 初始化low数组
memset(lowNum, 0x7f/3, sizeof(lowNum));
// 构建邻接表
for(int i = 1; i <= edgeCount; i++)
{
scanf("%d%d", &fromNode, &toNode);
nextEdge[i] = head[fromNode];
edgeTo[i] = toNode;
head[fromNode] = i;
}
// 对所有未访问的节点执行Tarjan算法
for(int i = 1; i <= nodeCount; i++)
{
if(!dfsNum[i])
{
seqCounter = 0;
tarjan(i);
}
}
// 计算每个强连通分量的出度
for(int i = 1; i <= nodeCount; i++)
{
for(int j = head[i]; j != -1; j = nextEdge[j])
{
if(component[i] != component[edgeTo[j]])
outDegree[component[i]]++;
}
}
// 统计出度为0的强连通分量
for(int i = 1; i <= componentCount; i++)
{
if(outDegree[i] == 0)
{
zeroOutComponentCount++;
result += componentSize[i];
}
}
// 输出结果:如果只有一个出度为0的分量,则输出其大小,否则输出0
if(zeroOutComponentCount == 1)
{
cout << result;
}
else
cout << 0;
return 0;
}