题目大意:
1、单向边的图里面,求最受欢迎的奶牛。
2、最受欢迎的奶牛是一个群体(一个环),要满足“最”这个条件就必须满足(其他点都能指向这个环,而这个环不能再指向其它点)。
思路分析:
1、图中的环,可以通过强连通找出来。
2、本题只要找到那个唯一的,没有出度的ans环,环里的点的个数,就是答案。
(因为这个ans环出度为0,就不追捧别人。他是唯一的“出度为0”,说明其他点都有出度,就都会指向他了。)
====================以下是强连通的代码思路,如果看不懂,直接看代码注解====================
既然本题是板子,那就介绍一下强连通的基础吧: 1、强连通=最大能相互到达的点的群(也可以理解为“环”或者"环的集合");
2、还有一些相关的概念:联通块、入度、出度等等;
3、强连通的代码实现依靠深搜完成:
3/1、定义每个点有2个参数,一个是时间戳,我用字母 i(很多题解用dfn),一个是自己团的老大,我用字母d(很多题解用low);
3/2、搜索之前,定义全部点的时间戳和老大都为 0,设置一个栈,用来做当前团队的缓存(找到环就会集体出栈);
3/3、for一次全部点,对于每个时间戳为0的点 i,表示从未被访问过,跑dfs(i);
3/4、时间戳是该点(x)被搜到的次序,老大一开始定义为自己的时间戳。
深搜 x->y,y有三种情况:
1、y的时间戳为0(说明未被访问过),直接搜他,回溯(找到环了,也就是访问在栈内的点再回头)时用y的老大更新x的老大;
2、y的时间戳不为0(以前被访问过),y在栈内,说明 y -> x是通的,现在 x再次搜到了y,说明 x->y也通,两者能到达,
并且y是栈内(说明y先找到x,x现在绕回到y),用y的时间戳更新x的老大(y是这个环的老大,当然就是x的老大);
3、y时间戳不为0,但又不在栈内。说明 y 是不通 x 的,所以 x通 y ,也不可能成环,所以什么事情也没有发生。
3/5、回溯时,如果 x 点的老大 还是 自己的时间戳(说明自己就是环里的老大)此时将环内的人出栈,完成。
====================以上是强连通的代码思路,如果看不懂,直接看代码注解====================
上代码:
#include<cstdio>
const int mx=20005;
int n,m,len=0,lx=0,lb=0;
int chu[mx];//用来数每个团队的出度(桶)
int bb[mx];//用来统计每个团队的人数(桶)
int l[mx],tou=0;//栈用
struct nod{int h,i,d,v,b;}a[mx];
//点:i是时间戳,d是老大,b是所在团队编号,v是是否在栈内,h是邻接表的last;
struct nod1{int x,y,gg;}b[mx*5]; //边:gg是next数组
void ins(int x,int y)
{
len++;b[len].x=x;b[len].y=y;b[len].gg=a[x].h;a[x].h=len;
}
void dfs(int x)
{ //时间戳 i不会再改;d会随时更新
a[x].i=a[x].d=++lx;//当前访问的第 lx个点
l[++tou]=x; a[x].v=1;//进栈
for(int i=a[x].h;i>0;i=b[i].gg)
{
int y=b[i].y;//对于y,分三种情况讨论
if(a[y].i==0)//情况1:未被访问的
{
dfs(y);//直接访问 y
if(a[y].d<a[x].d) a[x].d=a[y].d;//能回溯:说明已成环,y能问到更小的点,所以更新x点的信息;
}
else if(a[y].v==1)//情况2:y被访问过,还在栈内,说明 y->x是通的,现在 x又能找到y,所以成环了,
{
if(a[y].i<a[x].d) a[x].d=a[y].i;//y可能作为x这个环的老大,用y.i更新 x.d
}
//情况3:y被访问过,但不在栈内,那么 x->y,但是 y不通x ,不可能成环,所以什么事都没发生。
}
//回溯时,如果发现自己是团队老大,就要将栈内的整队人拉走,建立新团队
if(a[x].i==a[x].d)
{
lb++;bb[lb]=0;//诞生新团队编号
int k;
while(1)//拉人出栈
{
k=l[tou--]; a[k].v=0;
bb[lb]++;//统计本团的人数
a[k].b=lb;//更新参数 b
if(k==x) break;
}
}
}
int main()
{
scanf("%d %d",&n,&m);int x,y;
for(int i=1;i<=n;i++) //初始化点的信息
a[i].i=a[i].d=a[i].h=a[i].v=0;
for(int i=1;i<=m;i++)//构图
{
scanf("%d %d",&x,&y); ins(x,y);
}
for(int i=1;i<=n;i++)//强连通的核心代码
{
if(a[i].i==0) dfs(i);//时间戳为0,表示该点未被访问,开始搜
}
//强连通结束后,每个点的 b参数都得到了满足
//通过扫描所有的边,找出每个团队的出度情况
for(int i=1;i<=lb;i++) chu[i]=0;//初始化数出度的桶
for(int i=1;i<=len;i++)
{
x=b[i].x; y=b[i].y;
if(a[x].b!=a[y].b) //连接不同团队之间的边,才是度
{
chu[a[x].b]++;//x->y,所以是 x所在团队的出度(也叫y的入度)
}
}
//如果存在(唯一的出度为0的团队),就是答案,否则无解。
x=0;
for(int i=1;i<=lb;i++)
{
if(chu[i]==0) { x++; y=i;if(x>1) break;}
}
if(x==1) { printf("%d\n",bb[y]);}//有唯一的团队(出度为0的)
return 0;
}