[HAOI2006]受欢迎的牛

题目:BZOJ1051、洛谷P2341。

题目大意:给你一张有向图,问有多少个点能从其他任意点到达。

解题思路:首先求强连通分量,缩点。

然后就变成有向无环图上的问题了。

这里有一个巧妙的思路:找出度为0的点。

由于是有向无环图,任意一个能从其他点到达的点出度一定是0,否则就会有环。

而这样的点最多有一个,如果有多个这样的点,它们两两之间已经不连通,已经不符合题意。

所以求各个点的出度,当只有一个出度为0的点(强连通分量)时,输出它原来包含的点的个数,否则输出0即可。

C++ Code:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<set>
using namespace std;
#define N 10005
int n,m,head[N],dfn[N],low[N],sta[N],ys[N],sz[N],top,idx,ltfl;
bool vis[100005];
set<int>deg[N];
struct edge{
  int from,to,nxt;
}e[100005];
inline int readint(){
  char c=getchar();
  for(;!isdigit(c);c=getchar());
  int d=0;
  for(;isdigit(c);c=getchar())
  d=(d<<3)+(d<<1)+(c^'0');
  return d;
}
void tarjan(int now){
  vis[sta[++top]=now]=true;
  dfn[now]=low[now]=++idx;
  for(int i=head[now];i;i=e[i].nxt)
  if(!dfn[e[i].to]){
    tarjan(e[i].to);
    if(low[now]>low[e[i].to])
    low[now]=low[e[i].to];
  }else
  if(vis[e[i].to]&&low[now]>dfn[e[i].to])
  low[now]=dfn[e[i].to];
  if(dfn[now]==low[now]){
    ++ltfl;
    int k;
    do{
      vis[k=sta[top--]]=false;
      ys[k]=ltfl;
      ++sz[ltfl];
    }while(k!=now);
  }
}
int main(){
  top=ltfl=0;
  memset(head,0,sizeof head);
  n=readint(),m=readint();
  for(int i=1;i<=m;++i){
    int x=readint(),y=readint();
    e[i]=(edge){x,y,head[x]};
    head[x]=i;
  }
  memset(dfn,0,sizeof dfn);
  memset(low,0,sizeof low);
  for(int i=1;i<=n;++i)
  if(!dfn[i]){
    idx=0;
    memset(vis,0,sizeof vis);
    tarjan(i);
  }
  for(int i=1;i<=m;++i)
  if(ys[e[i].to]!=ys[e[i].from]&&!deg[ys[e[i].to]].count(ys[e[i].from]))
  deg[ys[e[i].from]].insert(ys[e[i].to]);
  int ans=0;
  bool b=false;
  for(int i=1;i<=ltfl;++i)
  if(!deg[i].size()){
    if(b){
      puts("0");
      return 0;
    }
    ans=sz[i];
    b=true;
  }
  printf("%d\n",ans);
  return 0;
}

 

posted @ 2017-11-01 13:20  Mrsrz  阅读(142)  评论(0编辑  收藏  举报