题目: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;
}