Tarjan(强连通分量缩点) - Popular Cows - POJ 2186
题意:
每一头牛的愿望就是变成一头最受欢迎的牛。
现在有 N 头牛,编号从 1 到 N,给你 M 对整数 (A,B),表示牛 A 认为牛 B 受欢迎。
这种关系是具有传递性的,如果 A 认为 B 受欢迎,B 认为 C 受欢迎,那么牛 A 也认为牛 C 受欢迎。
你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。
输入格式
第一行两个数 N,M;
接下来 M 行,每行两个数 A,B,意思是 A 认为 B 是受欢迎的(给出的信息有可能重复,即有可能出现多个 A,B)。
输出格式
输出被除自己之外的所有牛认为是受欢迎的牛的数量。
数据范围
1 ≤ N ≤ 1 0 4 , 1 ≤ M ≤ 5 × 1 0 4 1≤N≤10^4, 1≤M≤5×10^4 1≤N≤104,1≤M≤5×104
输入样例:
3 3
1 2
2 1
2 3
输出样例:
1
分析:
分 析 题 意 , 我 们 计 算 找 出 度 为 0 的 点 的 数 量 。 分析题意,我们计算找出度为0的点的数量。 分析题意,我们计算找出度为0的点的数量。
最 暴 力 的 做 法 : 反 向 建 图 , 接 着 从 每 个 点 开 始 搜 索 , 看 能 否 遍 历 剩 下 的 所 有 点 , 时 间 复 杂 度 是 O ( n × ( n + m ) ) 。 最暴力的做法:反向建图,接着从每个点开始搜索,看能否遍历剩下的所有点,时间复杂度是O(n×(n+m))。 最暴力的做法:反向建图,接着从每个点开始搜索,看能否遍历剩下的所有点,时间复杂度是O(n×(n+m))。
可 以 通 过 t a r j a n 算 法 对 整 个 图 求 强 连 通 分 量 进 行 缩 点 , 得 到 一 个 拓 扑 图 。 可以通过tarjan算法对整个图求强连通分量进行缩点,得到一个拓扑图。 可以通过tarjan算法对整个图求强连通分量进行缩点,得到一个拓扑图。
对 于 新 的 拓 扑 图 而 言 , 统 计 所 有 强 连 通 分 量 的 出 度 , 若 出 度 为 0 , 则 该 联 通 块 内 的 所 有 点 均 符 合 条 件 。 对于新的拓扑图而言,统计所有强连通分量的出度,若出度为0,则该联通块内的所有点均符合条件。 对于新的拓扑图而言,统计所有强连通分量的出度,若出度为0,则该联通块内的所有点均符合条件。
注意:
若 出 度 为 0 的 强 连 通 分 量 的 数 量 大 于 1 , 则 这 些 出 度 为 0 的 强 连 通 分 量 之 间 无 法 连 通 。 即 不 存 在 符 合 条 件 的 点 。 若出度为0的强连通分量的数量大于1,则这些出度为0的强连通分量之间无法连通。即不存在符合条件的点。 若出度为0的强连通分量的数量大于1,则这些出度为0的强连通分量之间无法连通。即不存在符合条件的点。
故 仅 当 出 度 为 0 的 强 连 通 分 量 的 数 量 等 于 1 时 , 才 存 在 符 合 条 件 的 点 。 故仅当出度为0的强连通分量的数量等于1时,才存在符合条件的点。 故仅当出度为0的强连通分量的数量等于1时,才存在符合条件的点。
点 的 数 量 即 该 强 连 通 分 量 中 点 的 数 量 。 点的数量即该强连通分量中点的数量。 点的数量即该强连通分量中点的数量。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10010, M=50010;
int n,m;
int e[M],ne[M],h[N],idx;
int stk[N],top;
bool in_stk[N];
int id[N],ssc_cnt;
int dfn[N],low[N],timestamp;
int dout[N],Size[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++timestamp;
stk[++top]=u,in_stk[u]=true;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(!dfn[j])
{
tarjan(j);
low[u]=min(low[j],low[u]);
}
else if(in_stk[j]) low[u]=min(dfn[j],low[u]);
}
if(dfn[u]==low[u])
{
++ssc_cnt;
int y;
do
{
y=stk[top--];
in_stk[y]=false;
id[y]=ssc_cnt;
Size[ssc_cnt]++;
}while(y!=u);
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
int a,b;
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
}
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=ne[j])
{
int k=e[j];
int a=id[i],b=id[k];
if(a!=b) dout[a]++;
}
int zeros=0,sum=0;//出度为0的强连通快数量和这些连通块中的点的总数
for(int i=1;i<=ssc_cnt;i++)
if(!dout[i])
{
zeros++;
sum+=Size[i];
if(zeros>1)
{
sum=0;
break;
}
}
printf("%d\n",sum);
return 0;
}