题目链接:http://poj.org/problem?id=2186
题意:
有一群绵羊,
现在有一些喜欢关系,喜欢关系是可以传递的。
问有多少绵羊直接或间接地被所有绵羊喜欢。
算法:
首先求强联通并缩点,方法见BYVoid的《有向图强连通分量的Tarjan算法》。
缩点之后形成了一个DAG。
现在来看一下有几个出度为0的块。
如果没有出度为0的块,
必然无解。
因为某个块有指向另一个块的边的话,那么那个块肯定就没有指向它的边或有向路径了,因为那样就形成了环。
如果出度为0的块多于一个,也无解。
因为显然从一个出度为零的块无法到达另一个出度为零的块。
如果有且只有一个出度为0的块,
这个块中的点即为解。
代码如下:
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
const int MAXN=11000;
int tot,blkn;
int low[MAXN],dfsn[MAXN],blk[MAXN];
bool ins[MAXN],dex[MAXN];
vector<int>map[MAXN];
vector<int>blk_map[MAXN];
stack<int>ss;
void dfs(int u)
{
dfsn[u]=tot++;
ins[u]=true;
low[u]=dfsn[u];
ss.push(u);
for(int i=0; i<map[u].size(); i++)
{
int v=map[u][i];
if(dfsn[v]!=-1)
{
if(ins[v])
{
low[u]=min(low[u],dfsn[v]);
}
}
else
{
dfs(v);
low[u]=min(low[u],low[v]);
}
}
if(dfsn[u]!=low[u])
return;
blkn++;
while(!ss.empty())
{
int v=ss.top();
ss.pop();
ins[v]=false;
blk[v]=blkn;
if(v==u)break;
}
return;
}
void blk_dfs(int u)
{
dfsn[u]=1;
for(int i=0; i<blk_map[u].size(); i++)
{
if(!dfsn[blk_map[u][i]])
blk_dfs(blk_map[u][i]);
}
return;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=0; i<n; i++)
{
map[i].clear();
blk_map[i].clear();
}
memset(ins,0,sizeof(ins));
memset(dex,0,sizeof(dex));
memset(blk,-1,sizeof(blk));
memset(dfsn,-1,sizeof(dfsn));
while(!ss.empty())
ss.pop();
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
if(u==v)
continue;
u--;
v--;
map[v].push_back(u);
}
blkn=0;
for(int i=0; i<n; i++)
if(blk[i]==-1)
{
tot=0;
dfs(i);
}
for(int i=0; i<n; i++)
{
for(int j=0; j<map[i].size(); j++)
{
int v=map[i][j];
if(blk[i]==blk[v])continue;
blk_map[blk[i]].push_back(blk[v]);
dex[blk[v]]=true;
}
}
int rt,cot=0;
for(int i=1; i<=blkn; i++)
{
if(!dex[i])
{
rt=i;
cot++;
}
}
if(cot>1)
{
puts("0");
continue;
}
memset(dfsn,0,sizeof(dfsn));
blk_dfs(rt);
{
int i;
for(i=1; i<=blkn; i++)
{
if(!dfsn[i])break;
}
if(i<=blkn)
{
puts("0");
continue;
}
}
int ans=0;
for(int i=0; i<n; i++)
{
if(blk[i]==rt)
{
ans++;
}
}
printf("%d\n",ans);
}
return 0;
}