题目链接:https://www.luogu.org/problem/P2341
题意:A喜欢B,B喜欢C,那么A就喜欢C,每个人都必定喜欢自己,求问被所有人喜欢的人有多少个
输入格式:n,m分别是人总数和喜欢关系的数目,接下来m行每行两个数字a,b代表a喜欢b
分析:输入格式方面很容易想到可以转化成图论题来做,在图上,A喜欢B就代表A可达B,强连通分量里面所有点是互相可达的,而强连通分量a只要有一个点可达强连通分量b,那么a中所有点都可达b,我们便利用Tarjan算法算出强连通分量并且记录每个强连通分量里点的个数,然后进行缩点。缩点后我们继续观察,发现如果出度为0的点只有一个,那么这肯定是个连通图,并且其他所有点可以达到该点,而该点无法到达其他点,那么这个点(缩点后的强连通分量)内点的个数就是答案,而若有两个以上的点出度为0,该图则不是联通图,答案为0.
直接套Tarjan算法缩点模板,记录数目并最后记录每个点出度即可即可。
#include<bits/stdc++.h> #define maxn 10001 using namespace std; vector<int>G[maxn]; stack<int>s; int n,m; int dfn[maxn],vis[maxn],low[maxn],color[maxn],num[maxn],colornum=0,res[maxn],cnt; //num记录每个颜色下点的数目,res记录每个点的出度 void paint(int x) { s.pop(); color[x]=colornum; num[colornum]++; vis[x]=false; } void tarjan(int x) { dfn[x]=low[x]=++cnt; s.push(x); vis[x]=true; for(int i=0;i<G[x].size();i++) { int q=G[x][i]; if (!dfn[q]) { tarjan(q); low[x]=min(low[x],low[q]); } else if (vis[q]) low[x]=min(low[x],dfn[q]); } if (low[x]==dfn[x]) { colornum++; while(s.top()!=x) { int t=s.top(); paint(t); } paint(x); } } int main() { cin>>n>>m; for(int i=1;i<=m;i++) { int u,v; cin>>u>>v; G[u].push_back(v); } for(int i=1;i<=n;i++) { if (!dfn[i]) tarjan(i); } int ans=0; for(int i=1;i<=n;i++) { for(int j=0;j<G[i].size();j++){ if(color[i]!=color[G[i][j]]){ res[color[i]]++; } } } for(int i=1;i<=colornum;i++){ if(ans==0&&res[i]==0){ ans=num[i]; } else if(res[i]==0){ ans=0;break; } } cout<<ans<<endl; return 0; }