pku2186 Popular Cows
Kosaraju算法的原理:先对逆图作一遍后序遍历,计算访问时间f[u](如果图是一个DAG(有向无环图),这一过程产生一个拓扑排序序列)。然后在原图上再一次DFS,不过是从f[u]最大的未访问点开始遍历。
这样得到的就是一个强连通分量了。因为当原图中w点可以到达v点的时候,在访问逆图的时,逆图中的dfs树中v才可能是w的父节点,这样,f[v]将大于f[w]。再在原图中作DFS,由于是优先访问具有最高后序编号的未访问的结点,如果在原图中v不可到达w,则v和w不可能在同一棵dfs树内,那么v和w不在同一连通分支中。就这么简单的方法。
注意到按照f[u]递减的顺序dfs实际上就是按照拓扑排序的逆序访问各顶点。
#include <iostream>
using namespace std;
#define MAXN 10001
struct Edge{
int v,next;
}edg[10*MAXN];
int p[MAXN],p1[MAXN],n,m,pcnt,post[MAXN],postcnt,c;
int visited[MAXN];
int cdeg[MAXN];
void dfs(int u){
int i,v;
for(i=p1[u];i!=-1;i=edg[i].next){
v=edg[i].v;
if(!visited[v]){
visited[v]=1;
dfs(v);
}
}
post[postcnt++]=u;
}
void dfs1(int u){
int i,v;
for(i=p[u];i!=-1;i=edg[i].next){
v=edg[i].v;
if(!visited[v]){
visited[v]=c;
dfs1(v);
}
}
}
int main(){
int i,j,u,v,cnt,ans,mark;
while(scanf("%d%d",&n,&m)!=EOF){
memset(p,-1,sizeof(p));
memset(p1,-1,sizeof(p1));
pcnt=0;
for(i=0;i<m;i++){
scanf("%d%d",&u,&v);
edg[pcnt].next=p[u];
edg[pcnt].v=v;
p[u]=pcnt++;
edg[pcnt].next=p1[v];
edg[pcnt].v=u;
p1[v]=pcnt++;
}
memset(visited,0,sizeof(visited));//反向图,求f[u]
postcnt=0;
for(i=1;i<=n;i++){
if(!visited[i]){
visited[i]=1;
dfs(i);
}
}
c=0;
memset(visited,0,sizeof(visited));//正向图
for(i=postcnt-1;i>=0;i--){
if(!visited[post[i]]){
visited[post[i]]=++c;//标记各SCC
dfs1(post[i]);
}
}
memset(cdeg,0,sizeof(cdeg));//计算各SCC的出度
for(i=1;i<=n;i++){
for(j=p[i];j!=-1;j=edg[j].next){
if(visited[i]!=visited[edg[j].v]){
cdeg[visited[i]]++;
}
}
}
cnt=0;
for(i=1;i<=c;i++){//计算出度为0的SCC个数
if(cdeg[i]==0){
cnt++;
mark=i;
}
}
if(cnt>1){
printf("0\n");
continue;
}
ans=0;
for(i=1;i<=n;i++){
if(visited[i]==mark)
ans++;
}
printf("%d\n",ans);
}
return 0;
}
using namespace std;
#define MAXN 10001
struct Edge{
int v,next;
}edg[10*MAXN];
int p[MAXN],p1[MAXN],n,m,pcnt,post[MAXN],postcnt,c;
int visited[MAXN];
int cdeg[MAXN];
void dfs(int u){
int i,v;
for(i=p1[u];i!=-1;i=edg[i].next){
v=edg[i].v;
if(!visited[v]){
visited[v]=1;
dfs(v);
}
}
post[postcnt++]=u;
}
void dfs1(int u){
int i,v;
for(i=p[u];i!=-1;i=edg[i].next){
v=edg[i].v;
if(!visited[v]){
visited[v]=c;
dfs1(v);
}
}
}
int main(){
int i,j,u,v,cnt,ans,mark;
while(scanf("%d%d",&n,&m)!=EOF){
memset(p,-1,sizeof(p));
memset(p1,-1,sizeof(p1));
pcnt=0;
for(i=0;i<m;i++){
scanf("%d%d",&u,&v);
edg[pcnt].next=p[u];
edg[pcnt].v=v;
p[u]=pcnt++;
edg[pcnt].next=p1[v];
edg[pcnt].v=u;
p1[v]=pcnt++;
}
memset(visited,0,sizeof(visited));//反向图,求f[u]
postcnt=0;
for(i=1;i<=n;i++){
if(!visited[i]){
visited[i]=1;
dfs(i);
}
}
c=0;
memset(visited,0,sizeof(visited));//正向图
for(i=postcnt-1;i>=0;i--){
if(!visited[post[i]]){
visited[post[i]]=++c;//标记各SCC
dfs1(post[i]);
}
}
memset(cdeg,0,sizeof(cdeg));//计算各SCC的出度
for(i=1;i<=n;i++){
for(j=p[i];j!=-1;j=edg[j].next){
if(visited[i]!=visited[edg[j].v]){
cdeg[visited[i]]++;
}
}
}
cnt=0;
for(i=1;i<=c;i++){//计算出度为0的SCC个数
if(cdeg[i]==0){
cnt++;
mark=i;
}
}
if(cnt>1){
printf("0\n");
continue;
}
ans=0;
for(i=1;i<=n;i++){
if(visited[i]==mark)
ans++;
}
printf("%d\n",ans);
}
return 0;
}