Description
每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎。
这种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎。你的任务是求出有多少头牛被所有的牛认为是受欢迎的。
Input
第一行两个数N,M。 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可能出现多个A,B)
Output
一个数,即有多少头牛被所有的牛认为是受欢迎的。
Sample Input
3 3
1 2
2 1
2 3
Sample Output
1
HINT
100%的数据N<=10000,M<=50000
Source
首先建图,根据关系来连边(A认为B受欢迎=A→B)。“必须被所有的牛欢迎”这个条件相当苛刻,那么什么情况下最终的答案会>1?这种情况只会出现在一个强连通分量中(互相觉得对方受欢迎)(当然一个单独结点也可视为一个强连通分量),这个强连通分量中每个节点都是答案,仔细想一想就会发现最终构成答案的强连通分量只会有一个,而且强连通分量中如果有一头牛认为某一头牛受欢迎,那么就可以得到这个强连通分量中所有牛就都认为这头牛受欢迎。利用这个性质,我们就可以依靠强连通分量的算法。
注意:next在某些版本编译器中为关键字,可能导致CE,使用next数组务必小心
方法一:Tarjan后进行DFS并统计次数,若某个强连通分量能被其它所有强连通分量访问到,那它就是答案。
#include
#include
#include
#include
using namespace std;
const int N=10010;
const int M=50010;
int n,m,co,index1,num,next1[M],point[N],to[M];
int dfn[N],low[N],t[N],stack[N],top,ans[N],ans1;//i结点所在的强连通分量为t[i],ans[i]统计第i个强连通分量的结点数
bool vis[N],vis1[N],vis2[N],instack[N];int ans2[N];
void in(int &x)
{
char t=getchar();int f=1;x=0;
while((t<48)or(t>57)){if(t=='-')f=-1;t=getchar();}
while((t>=48)and(t<=57)){x=x*10+t-48;t=getchar();}
x*=f;
}
void add(int from,int to1)
{
++co;
next1[co]=point[from];
point[from]=co;
to[co]=to1;
}
void tarjan(int u)
{
vis[u]=instack[u]=1;
stack[++top]=u;
low[u]=dfn[u]=++index1;
for (int now=point[u];now;now=next1[now])
{
int v=to[now];
if (!vis[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else
if (instack[v]) low[u]=min(low[u],dfn[v]);
}
if (dfn[u]==low[u])
{
++num;int v=0;
while(v!=u)
{
v=stack[top--];
t[v]=num;++ans[num];
instack[v]=0;
}
}
}
void dfs(int now,int now1)
{
vis[now]=1;
if ((t[now]!=now1)and(!vis1[t[now]]))
{++ans2[t[now]];vis1[t[now]]=1;}
for (int i=point[now];i;i=next1[i])
{
int v=to[i];
if (!vis[v]) dfs(v,now1);
}
}
int main()
{
in(n),in(m);
for (int i=1;i<=m;++i)
{
int x,y;
in(x),in(y);
add(x,y);
}
for (int i=1;i<=n;++i)
if (!vis[i]) tarjan(i);
for (int i=1;i<=n;++i)
if (!vis2[t[i]])
{
memset(vis,0,sizeof(vis));
memset(vis1,0,sizeof(vis1));
dfs(i,t[i]);vis2[t[i]]=1;
}
for (int i=1;i<=n;++i)
if (ans2[t[i]]==(num-1))
{ans1=ans[t[i]];break;}
printf("%d",ans1);
return 0;
}
方法二:利用强连通分量来缩点,把整个强连通分量视为一个结点并重新建图连边,出度为0者即为答案。
注意:可能出现某一个强连通分量被孤立的情况(入度出度都为0),解决方法是把所有强连通分量都扫一遍,如果出现多个出度为0的强连通分量则输出0。
#include
#include
#include
#include
using namespace std;
const int N=10010;
const int M=50010;
int n,m,co,co1,index1,num,next1[M],point[N],to[M];
int dfn[N],low[N],t[N],stack[N],top,ans[N],ans1;
bool vis[N],instack[N];int point2[N],next2[M],to2[M];
void in(int &x)
{
char t=getchar();int f=1;x=0;
while((t<48)or(t>57)){if(t=='-')f=-1;t=getchar();}
while((t>=48)and(t<=57)){x=x*10+t-48;t=getchar();}
x*=f;
}
void add(int from,int to1)
{
++co;
next1[co]=point[from];
point[from]=co;
to[co]=to1;
}
void add1(int from,int to1)
{
++co1;
next2[co1]=point2[from];
point2[from]=co1;
to2[co1]=to1;
}
void tarjan(int u)
{
vis[u]=instack[u]=1;
stack[++top]=u;
low[u]=dfn[u]=++index1;
for (int now=point[u];now;now=next1[now])
{
int v=to[now];
if (!vis[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else
if (instack[v]) low[u]=min(low[u],dfn[v]);
}
if (dfn[u]==low[u])
{
++num;int v=0;
while(v!=u)
{
v=stack[top--];
t[v]=num;++ans[num];
instack[v]=0;
}
}
}
void work(int u)
{
for (int i=point[u];i;i=next1[i])
{
int v=to[i];
if (t[v]!=t[u]) add1(t[u],t[v]);
}
}
int main()
{
in(n),in(m);
for (int i=1;i<=m;++i)
{
int x,y;
in(x),in(y);
add(x,y);
}
for (int i=1;i<=n;++i)
if (!vis[i]) tarjan(i);
for (int i=1;i<=n;++i) work(i);
for (int i=1;i<=num;++i)
if (!point2[i])
if (ans1) {ans1=0;break;}
else ans1=ans[i];
printf("%d",ans1);
return 0;
}