Description
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.
Input
* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.
Output
Sample Input
3 3 1 2 2 1 2 3
Sample Output
1
题目大意:
有n只牛,牛A认为牛B很牛,牛B认为牛C很牛。给你M个关系(谁认为谁牛),求大家都认为它很牛的牛有几只。PS:如果牛A认为牛B很牛,牛B认为牛C很牛。那么我们就认为牛A认为牛C很牛。
ps:用到求有向图的强连通分量的知识和缩点的知识,不会的进http://www.cppblog.com/sosi/archive/2010/09/26/127797.aspx我感觉这个讲的很好,通俗易懂。
用tarjan算法dfs后会得到一棵树(因为是有向图,要把每个未访问的节点作为根节点进行dfs),用上面博客里介绍的方法求出连通分量,然后缩点,在对缩点进行建树(此题不用,因为只需要求缩点的出度数),缩点出度为0的联通块就是要求的答案,如果有多个出度为0的,则表示每个节点无法两两到达,也就不存在任何一点都可以到该点。当然,在计算前先判断一下是否连接每一个点(无向图的联通分量唯一)。
为什么度为0的缩点所代表的的联通块都是要求的点?
因为:A->B B->C 那么dfs后就得到A->B->C这样的连线,此时正符合题意的传递性,假设A,B,C 代表的是所点后的,那么对于C而言出度为0,也就是说对于缩点块外的点都能到达该缩点的点,而对于每一个缩块而言,因为根据他的性质,之所以能构成缩点,是因为他们属于同一个联通分量,即在同一个强联通图中,而强联通图内的点两两互达。
注意:对于有向图,不需要判断是否为重复走的父节点,在这里调试了好久
#include <iostream>
#include <cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<vector>
using namespace std;
int arr[10005];///判断是否为一个联通分量
int low[10005],dfn[10005];///tarjan算法
int vis[10005];///标记
int svis[10005];///标记节点是否在栈内
int bcc[10005];///缩点
int top;
stack<int >s;///栈
vector<int >eage[10005];///存图
vector<int >tree[100005];///存树
int n,m;
int ant;
int f(int x)
{
int r,y;
r=y=x;
while(r!=arr[r])r=arr[r];
int t;
while(r!=y)
{
t=arr[y];
arr[y]=r;
y=t;
}
return r;
}
int h=0;
void tarjan(int u)
{
//cout<<"u "<<u<<endl;
low[u]=dfn[u]=ant++;
s.push(u);
svis[u]=1;///u入栈
vis[u]=1;
for(int i=0; i<eage[u].size(); i++)
{
//cout<<"jinqu"<<endl;
int v=eage[u][i];
// cout<<"zheli"<<endl;
if(!vis[v])
{
// cout<<"lksjdafhld"<<endl;
tarjan(v);
// cout<<low[v]<<" low[v]"<<endl;
// cout<<"*low[u] "<<u<<" "<<low[u]<<endl;
low[u]=min(low[u],low[v]);
}
// cout<<"zhene"<<endl;
//cout<<"svis[v] "<<svis[v]<<"v "<<v<<endl;
if(svis[v])///v在栈内
{
//cout<<dfn[v]<<" dfn[v]"<<endl;
// cout<<"***low[u] "<<u<<" "<<low[u]<<endl;
low[u]=min(low[u],dfn[v]);
}
}
//cout<<"&&"<<endl;
//cout<<"u "<<u<<" pre ***"<<endl;
//cout<<"dfn[u] "<<dfn[u]<<" low[u] "<<low[u]<<endl;
if(dfn[u]==low[u])
{
h++;
top++;
while(!s.empty())
{
if(s.top()==u)break;
int k=s.top();
s.pop();
svis[k]=0;
bcc[k]=top;
}
if(!s.empty())
{
int k=s.top();
s.pop();
svis[k]=0;
bcc[k]=top;
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=0; i<=n; i++)eage[i].clear(),tree[i].clear();
while(!s.empty())s.pop();
for(int i=0; i<=n; i++)
{
arr[i]=i;
vis[i]=0;
svis[i]=0;
bcc[i]=i;
}
int x,y;
for(int i=0; i<m; i++)
{
scanf("%d%d",&x,&y);
if(f(x)!=f(y))arr[f(x)]=f(y);
eage[x].push_back(y);
}
int ans=0;
for(int i=1; i<=n; i++)if(arr[i]==i)ans++;
if(ans!=1)
{
printf("0\n");
continue;
}
ant=1;
top=0;
for(int i=1; i<=n; i++)
{
if(!vis[i])
{
tarjan(i);
}
}
//cout<<"h "<<h<<endl;
int deg[10005];
memset(deg,0,sizeof(deg));
for(int i=1; i<=n; i++)
{
for(int j=0; j<eage[i].size(); j++)
{
if(bcc[i]!=bcc[eage[i][j]])
{
deg[bcc[i]]++;
}
}
}
memset(vis,0,sizeof(vis));
int sum=0;
//cout<<"top "<<top<<endl;
for(int i=1; i<=top; i++)
{
if(!deg[i])
{
sum++;
x=i;
}
}
// cout<<sum<<" sum"<<endl;
if(sum==1)
{
int ans=0;
for(int i=1; i<=n; i++)
{
if(bcc[i]==x)ans++;
}
printf("%d\n",ans);
}
else printf("0\n");
}
return 0;
}