题目传送门:【BZOJ 1051】 【POJ 2186】 (两道题是一样的)
题目大意:每一头牛的愿望就是变成一头最受欢迎的牛。现在有 N 头牛,给你 M 对整数 ( A , B ),表示牛 A 认为牛 B 受欢迎。 这
种关系是具有传递性的,如果 A 认为 B 受欢迎,B 认为 C 受欢迎,那么牛 A 也认为牛 C 受欢迎。你的任务是求出有多少头
牛被所有的牛认为是受欢迎的。(0 ≤ N ≤ 10000 , 0 ≤ M ≤ 50000 , ( A , B ) 可能有重复)
题目分析:
一道非常经典的求强连通分量以及缩点的题。许多 PPT 讲稿里面都会出现这道题。
由题,我们把每头牛抽象成一个点,牛与牛之间的关系抽象成单向边。这道题要求判断能够被“其他所有的点到达”的点的数量。
分析题意,只有当一个点的出度为 0,并且与所有其他点连通时才符合题目要求。注意到这个点可以只是一个“点”,也可以是“缩点”之后形成的点。因此,我们首先跑一遍 Tarjan,求出这个图的强连通分量;之后对每个强连通分量缩点,形成一个 DAG,之后根据这个 DAG 求出出度为 0 的点的数量。如果数量恰好为 1,那么答案就是这个点(同上,这个点既可以是一个“点”,也可以是一个强连通分量;前者答案为 1,后者答案为强连通分量的大小);反之,如果数量大于 1,则说明肯定有些点不连通(也就是说一头牛总是得不到某些牛的欢迎),那么此时答案为 0。
缩点的方法:求出强连通分量后,设每个点都有一个 belong[ i ];枚举 1-n 的点,判断和它相连的点 j 的 belong[ ] 是否相等;如果相等,说明在同一强连通分量中,跳过;否则,说明它们不在同一个强连通分量中,那么对它们连一条边。
下面附上代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
const int MX=100005;
struct Edge{
int to,len,next;
};
Edge edge[5*MX];
int n,m,head[MX],now=0;
int low[MX],dfn[MX],_index=0,belong[MX],_size[MX];//_size:这个强连通分量的大小
int out[MX],cnt=0; //out:出度大小 cnt:强连通分量的数量
stack<int> s;
bool ins[MX];
void adde(int u,int v){
edge[++now].to = v;
edge[now].next = head[u];
head[u] = now;
}
void tarjan(int u){
low[u] = dfn[u] = ++_index;
s.push(u);
ins[u] = true;
for (int i = head[u]; i ;i = edge[i].next){
int v = edge[i].to;
if (!dfn[v]){
tarjan(v);
if (low[v] < low[u]){
low[u] = low[v];
}
} else if (ins[v] && dfn[v] < low[u]){
low[u] = dfn[v];
}
}
if (low[u] == dfn[u]){
cnt++;
int tmp;
do{
tmp = s.top();
s.pop();
belong[tmp] = cnt;
ins[tmp] = false;
}while (tmp != u);
}
}
void solve(){
for (int i = 1;i <= n;i++){
_size[belong[i]]++;
}
for (int i = 1;i <= n;i++){
for (int j = head[i]; j ;j = edge[j].next){
int v = edge[j].to;
if (belong[i] != belong[v]){
out[belong[i]]++;
}
}
}
}
int main(){
int a,b;
cin>>n>>m;
if (m == 0){ //没有牛认为其他的牛受欢迎,直接退出
printf("0");
return 0;
}
for (int i = 1;i <= m;i++){
cin>>a>>b;
adde(a,b);
}
for (int i = 1;i <= n;i++){
if (!dfn[i])
tarjan(i);
}
solve();
int flag = 0;
bool used = false;
for (int i = 1;i <= cnt;i++){
if (out[i] == 0){
if (used){
printf("0");
return 0;
} else {
flag = i;
used = true;
}
}
}
printf("%d",_size[flag]);
return 0;
}