版权声明:布呗之路的守望者 https://blog.csdn.net/hypHuangYanPing/article/details/81986056
/**
链接:http://poj.org/problem?id=2186
缩点 tarjan求联通分量;
题意:n头牛,m个崇拜关系,并且崇拜具有传递性,a崇拜b ,b崇拜c --> a崇拜c,问最后有几头牛被崇拜;
有向图
显然在一个强连通分量内的所有点都是满足条件的,对整张图进行缩点后,将原图变为有向无环图(DAG)
对于缩点后的图来说,必然存在一个及一个以上的点的出度为0,必然不存在两个点的出度为0;
对于由强联通分量缩成的的点,应该输出强联通分量中点的个数;
缩点是利用tarjan求解强联通分量的,对于一些贡献是存在传导性;
*/
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1e5+7;
int top,bcnt,dcnt;
int sta[maxn],dfn[maxn],low[maxn],belong[maxn];
bool ins[maxn];
int out[maxn];
int u,v,n,m;
/**
dfn[maxn]:dfs遍历的编号
sta[maxn]:当前路径节点存储,记录所有可能构成强连通分量的点;
ins[maxn]:当前节点是否在栈内;
low[maxn]:回溯最远的节点编号;
belong[a] = b 表示原图上的节点 a 被缩至新图上的节点 b
out[maxn] 缩点后每个点的出度
G为原图 GG 为缩点后的图(DAG)
缩点后的图总共存在bcnt个点 下标范围 [1,bcnt];
使用时建完图后可直接调用tarjan函数
*/
std::vector<int> G[maxn];
std::vector<int> GG[maxn];
void dfs(int x){
dfn[x]=low[x]=++dcnt;//第几个被dfs到
sta[top++]=x;//将当前节点压栈
ins[x]=true;//是否在栈内
for(int pos:G[x]){
if(dfn[pos]==0) dfs(pos),low[x]=min(low[x],low[pos]);//对于未遍历的点 回溯时直接将该条路径上的low最小化 也就是能够表示的最小编号;
else if(ins[pos]) low[x]=min(low[x],dfn[pos]);
/**对于已经遍历的点,如果存在于栈中的话 由于当前是存在边的
因此需要和当前节点取最小即可;那为什么不和上面一样呢 由于当前节点不一定和已经遍历的点能构成环的情况,由于存在通路的情况 所以和dfn进行最小化
*/
}
if(dfn[x]==low[x]) {//如果当前能够回溯的值 和当前的编号一样的话 那么当前路径必然是存在环的.那么由于sta是记录路径的 因此直接输出到和当前相等即可;
++bcnt;
int pos;
do{
pos=sta[--top];
ins[pos]=false;
belong[pos]=bcnt;
}while(pos!=x);
}
}
void tarjan(){
bcnt=top=dcnt=0;
memset(dfn,0,sizeof(dfn));
memset(ins,0,sizeof(ins));
memset(out,0,sizeof(out));
for(int i=1;i<=n;i++) if(dfn[i]==0) dfs(i);
for(int i=1;i<=n;i++) GG[i].clear();
for(int i=1;i<=n;i++){
for(int pos:G[i]){
int u=belong[i];
int v=belong[pos];
if(u!=v) GG[u].push_back(v),++out[u];//记录缩点后的图每个带你的出度;
}
}
}
int main () {
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d %d",&u,&v);
G[u].push_back(v);
}
tarjan();
int cnt=0,pos;
for(int i=1;i<=bcnt;i++) if(out[i]==0) ++cnt,pos=i;
if(cnt!=1) { printf("0\n"); return 0;}//必然是不存在两个出度为0的情况 那么这样的条件必然是不满足条件的 具体条件可自行模拟即可;
cnt=0;
for(int i=1;i<=n;i++) if(belong[i]==pos) ++cnt;//记录缩点后图的强联通分量的size;
printf("%d\n",cnt);
return 0;
}