BZOJ1051: [HAOI2006]受欢迎的牛

来源:互联网 发布:无人机人工智能 编辑:程序博客网 时间:2024/05/18 10:38

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<cstdio>#include<algorithm>#include<cstring>#include<iostream>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<cstdio>#include<algorithm>#include<cstring>#include<iostream>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;}
1 0
原创粉丝点击