题目描述
每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶
牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜
欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你
算出有多少头奶牛可以当明星。
输入输出格式
输入格式:
第一行:两个用空格分开的整数:N和M
第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B
输出格式:
第一行:单独一个整数,表示明星奶牛的数量
输入输出样例
输入样例#1:
3 3
1 2
2 1
2 3
输出样例#1:
1
说明
只有 3 号奶牛可以做明星
【数据范围】
10%的数据N<=20, M<=50
30%的数据N<=1000,M<=20000
70%的数据N<=5000,M<=50000
100%的数据N<=10000,M<=50000
题目概要
给出一张有向图,求出这样的结点的个数:这种结点满足从图中任意一点出发均可到达该点
思路
看一看数据范围, O(n2) O ( n 2 ) 的暴力是肯定不可以的,再一想,一个强连通分量可以互达,而题目中只要求判断到达与否,而非权值
所以自然地使用到强连通分量缩点,本人偏爱使用tarjan (因为无脑)
至于这题的缩点,由于只要求判断到达与否,用一个映射表表示缩点后重新建图的对应点,最后遍历一遍边,求出所有点的出度(指向自己的边忽略)
至于为什么只用求出度,可以感性地想一想,缩点后的图中不可能有两个点及以上的强连通分量(因为这样的话,它们就会被缩成一个点)
那么若没有点有出度,图就是一个点,或几个点,输出n或0
若出度为0的点的个数大于1,那么指向不可能集中到一只牛身上,输出0
若只有一个点出度为零,那么明星就是这个点的扩展点数量
顺带拿这题练了练对拍
感谢同机房dalao cgz 对Ubuntu下对拍的大力支持
极丑的代码
#include<bits/stdc++.h>
using namespace std;
#define cl(x) memset(x,0,sizeof(x))
template <typename _Tp> inline void read(_Tp &x){
char c11=getchar();x=0;bool booo=0;
while(c11<'0'||c11>'9'){if(c11=='-')booo=1;c11=getchar();}
while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
if(booo)x=-x;
return ;
}
const int maxn=10050,maxm=50050;
struct node {int v,nxt;} a[maxm];
int head[maxn],p=0;
int low[maxn],dfn[maxn];
bool vis[maxn];
int num__=0;
int sta[maxn],top=0;
int colour=0;
int leaf[maxn];
int A[maxm],B[maxm];
int tot[maxn];
struct node1 {int v,nxt;} b[maxm];
int head1[maxn],pp=0;
int es[maxn];
int n,m;
inline void app(int x,int y);
inline void add(int u,int v);
void init();
void again();
void tarjan(int x){
low[x]=dfn[x]=++num__;
vis[x]=1;
sta[++top]=x;
for(int i=head[x];i;i=a[i].nxt){
int v=a[i].v;
if(!dfn[v]){
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(vis[v])low[x]=min(low[x],dfn[v]);
}
if(dfn[x]==low[x]){
vis[x]=0;
leaf[x]=++colour;
es[colour]++;
for(;sta[top]!=x;top--){
leaf[sta[top]]=colour;
vis[sta[top]]=0;
es[colour]++;
}
top--;
}
return ;
}
void work(){
int temp=0,tem;
for(int i=1;i<=colour;i++)
if(!tot[i]){
temp++;
tem=i;
}
if(temp!=1)putchar('0');
else printf("%d",es[tem]);
return ;
}
int main(){
init();
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
again();
work();
return 0;
}
void init(){
cl(head);cl(low);cl(dfn);cl(vis);cl(tot);cl(es);
read(n);read(m);
for(int i=1;i<=m;i++){
read(A[i]);read(B[i]);
add(A[i],B[i]);
}
return ;
}
inline void app(int x,int y){
if(leaf[x]==leaf[y])return ;
b[++pp].v=leaf[y];
b[pp].nxt=head1[leaf[x]];
head1[leaf[x]]=leaf[y];
tot[leaf[x]]++;
return ;
}
void again(){
for(int i=1;i<=m;i++){
app(A[i],B[i]);
}
return ;
}
inline void add(int u,int v){
a[++p].v=v;
a[p].nxt=head[u];
head[u]=p;
return ;
}