题目链接:http://poj.org/problem?id=2186
题面
题意
有
N
N
N只牛,有
M
M
M条关系,每条关系(A
i
_i
i,B
i
_i
i)描述了A
i
_i
i牛认为B
i
_i
i牛受欢迎。这个关系是可传递的,即若A牛认为B牛受欢迎,B牛认为C牛受欢迎那么A牛也认为C牛受欢迎别问我为什么可传递,问就是题目需要。求所有牛都认为受欢迎的牛的数量。
思路
如果把(A
i
_i
i,B
i
_i
i)看成A
i
_i
i连向B
i
_i
i的一条有向边,那么我们可以以这个方式建图,目标是找到所有满足下列条件的点:
从
图
中
任
意
一
点
出
发
均
能
到
达
该
点
从图中任意一点出发均能到达该点
从图中任意一点出发均能到达该点
看起来不太好做
换个方向,我们可以反向连边。
这样目标就是找到所有满足下列条件的点:
从
该
点
出
发
能
到
达
图
中
任
意
一
点
从该点出发能到达图中任意一点
从该点出发能到达图中任意一点
那么我们可以对每个点进行一遍广搜来判断
如果直接对每个点广搜,时间复杂度为
O
(
N
2
)
O(N^2)
O(N2)显然是会超时的。
所以我们考虑先找到图中的每个强连通分量(参考这个【教程】连通分量、强连通分量以及双连通分量),再考察这些强连通分量之间的关系。
对这些强连通分量进行拓扑排序:
- 若存在2个或以上入度为0的强连通分量:那么这些入度为0强连通分量都无法从强连通分量外部到达,所以不存在能到达图中任意一点的点集,答案为0。
- 若存在1个入度为0的强连通分量:那么这个强连通分量内的点都可以到达图中任意一点,答案为这个强连通分量内的点的数量。
- 若不存在入度为0的强连通分量:
肯定是你哪里搞错了不可能的。
AC代码
#include<stdio.h>
#include<stack>
#include<algorithm>
#define N 10005
#define M 50005
typedef long long LL;
using namespace std;
stack<int>sta;
int head[N],nxt[M],p[M],vis[N],dfn[N],low[N],f[N];
int rd[N],mi[N],get[N];
int m,cot,n,cnt,Case=0,ans=0,cat,x,y,pos;
void dfs(int now)
{
sta.push(now);
for(int i=head[now];i;i=nxt[i])
{
if(!vis[p[i]])
{
vis[p[i]]=1;dfn[p[i]]=low[p[i]]=++cat;
dfs(p[i]);
low[now]=min(low[now],low[p[i]]);
}else
if(!f[p[i]]) low[now]=min(low[now],low[p[i]]);
}
if(dfn[now]==low[now])
{
++cot;
while(sta.top()!=now)
{
f[sta.top()]=cot;
sta.pop();
mi[cot]++;
}
f[sta.top()]=cot;
sta.pop();
mi[cot]++;
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
p[++cnt]=x;nxt[cnt]=head[y];head[y]=cnt;
}
for(int i=1;i<=n;i++)
if(!vis[i])
{
vis[i]=1;
dfn[i]=++cat;low[i]=dfn[i];
dfs(i);
}
//找所有强连通分量
for(int i=1;i<=n;i++)
{
for(int j=head[i];j;j=nxt[j])
if(f[i]!=f[p[j]])
{
rd[f[p[j]]]++;
}
}//以找到的强联通分量重新建图
for(int i=1;i<=n;i++)
{
if(!rd[f[i]])
{
if(pos==f[i]||!pos)pos=f[i];
else
{
printf("0");return 0;
}
}
}
for(int i=1;i<=n;i++)
{
if(f[i]==pos)
{
for(int j=head[i];j;j=nxt[j])
{
get[f[p[j]]]=1;
}
}
}
for(int i=1;i<=cot;i++)
if(pos!=i&&!f[i])
{
printf("0");return 0;
}
printf("%d",mi[pos]);
return 0;
}
另外似乎确实有玄学优化可以直接广搜找答案但是我太菜了不会写。