haoi2006_受欢迎的牛_Solution

时间:2023-03-19 23:52:44

Brief Solution:

强连通tarjan+压缩点+判断是否除了一个点,其它点都有出度

Detailed Solution:

把牛看成点
若一个点b能到达点a,则b认为a受欢迎
若所有的点都能到达点a,则a被所有的牛欢迎

对于某个强连通中的点,任意两点可互达,互相受欢迎
对图求强连通,并把强连通压缩成一个点
若点a向与点a不在同一个强连通集合的点b,则点a所在的集合指向点b所在的集合(边)

若一个强连通集合的点(新图的点A)能被所有的点到达,则新图所有的点能到达点A
此时新图没有环,若一个点A能被所有的点到达,则除了该点,其它点的出度都不为0(图必有没有出度的点,因为图没有环)
则能被所有的点到达的点只有一个,否则会有环,矛盾
(在没有环的条件下,图中所有的点到汇集(到达)该点)

Code:

 #include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <malloc.h>
#define maxn 10000
#define maxm 50000 struct node
{
long d;
struct node *next;
}*info[maxn+];
long x[maxm+],y[maxm+];
long dfn[maxn+],low[maxn+],stack[maxn+],num[maxn+],ans[maxn+],count=,sum=;
bool vis[maxn+],vis_stack[maxn+],next[maxn+]; long min(long a,long b)
{
if (a>b)
return b;
else
return a;
} void tarjan(long d)
{
vis[d]=false;
count++;
stack[count]=d;
dfn[d]=count;
low[d]=count;
struct node *p;
long nd,pre;
p=info[d];
while (p)
{
nd=p->d;
if (vis[nd]==true)
{
tarjan(nd);
low[d]=min(low[d],low[nd]);
}
else if (vis_stack[nd]==true)
low[d]=min(low[d],dfn[nd]);
p=p->next;
}
pre=count;
if (dfn[d]==low[d])
{
sum++;
while (d!=stack[count])
{
num[stack[count]]=sum;
vis_stack[stack[count]]=false;
count--;
}
num[stack[count]]=sum;
vis_stack[stack[count]]=false;
count--;
ans[sum]=pre-count; //count+1~pre
}
} int main()
{
long i,n,m,d;
struct node *p;
scanf("%ld%ld",&n,&m);
// for (i=1;i<=n;i++)
// info[i]=NULL;
for (i=;i<=m;i++)
{
scanf("%ld%ld",&x[i],&y[i]);
p=(struct node *) malloc (sizeof(struct node));
p->d=y[i];
p->next=info[x[i]];
info[x[i]]=p;
}
for (i=;i<=n;i++)
{
vis[i]=true;
vis_stack[i]=true;
}
for (i=;i<=n;i++)
if (vis[i]==true)
tarjan(i);
for (i=;i<=sum;i++)
next[i]=false;
for (i=;i<=m;i++)
if (num[x[i]]!=num[y[i]])
next[num[x[i]]]=true;
d=;
for (i=;i<=sum;i++)
if (next[i]==false)
{
if (d==)
d=i;
else
{
d=-;
break;
}
}
if (d==-)
printf("0\n");
else
printf("%ld\n",ans[d]);
return ;
}