感言:
瑟瑟发抖的萌新好不容易A掉了这道14个人才过掉的题,然而被杨树神无情抢沙发,仅仅慢了十几分钟……已经哭晕在厕所……
为什么要拉出来这道题呢,因为刚刚刷完割边割点强连通分量,所以就写写这道题试一试,而且发现了一些新的技巧,神奇的缩点和最后答案的判断……
最受欢迎的牛
题目描述:
每头牛都有一个梦想:成为一个群体中最受欢迎的名牛!在一个有N(1<=N<=10,000)头牛的牛群中,给你M(1<=M<=50,000)个二元组(A,B),表示A认为B是受欢迎的。既然受欢迎是可传递的,那么如果A认为B受欢迎,B又认为C受欢迎,则A也会认为C是受欢迎的,哪怕这不是十分明确的规定。你的任务是计算被所有其它的牛都喜欢的牛的个数。
输入:
第一行,两个数,N和M。第2~M+1行,每行两个数,A和B,表示A认为B是受欢迎的;
输出:
一行,最受欢迎的奶牛的数量……
题解:
这道题用到了tarjan算法,用这个算法来求出所有的联通分量,为什么要求联通分量呢,因为每一个连通分量中,因为我喜欢你,你也喜欢我,那么我们两个就可以看成一个点,这样就可以吧五万个点缩成不同的连通分量,大大减少运算。
然后缩成了连通分量之后,就可以把他们当成每一个点,如果图中有且仅有一个点的出度为零,那么这个点中的所有元素就都是万众仰慕的牛了!输出元素数量就好。
那么为什么有且仅有一个点的出度为零就确定了这个集合呢,因为如果出度如果没有一个为零,那么这个图就仅仅有一个点,点里面包含的所有点都是满足条件的点,所有的牛都喜欢对方(形成了环),如果有多个点出度为零,那么就没有集合是满足条件的了,因为如果多个点出度为零,那么一定有其中的一个点不喜欢其他牛,那么就没有办法找到所有牛都喜欢的牛了,因此我们只需要判断点的出度就可以了。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; struct Edge { int x; int y; int next; }; Edge a[50001]; int n,m,xx,yy,Link[10001],len=0; int low[50001],dfn[50001],ind=0; int stack[50001],top=0,tot=0; int all[50001],bel[50001]; int Out_point[50001],ans; bool ins[50001],GG=false; void init(int xx,int yy) { a[++len].x=xx; a[len].y=yy; a[len].next=Link[xx]; Link[xx]=len; } void tarjan(int x) { low[x]=dfn[x]=++ind; stack[++top]=x; ins[x]=true; for(int i=Link[x];i!=0;i=a[i].next) { int y=a[i].y; if(!dfn[y]) { tarjan(y); low[x]=min(low[y],low[x]); }else if(ins[y]==true) low[x]=min(low[x],low[y]); } if(low[x]==dfn[x]) { int k; tot++; do { all[tot]++; k=stack[top--]; ins[k]=false; bel[k]=tot; }while(k!=x); } } int main() { memset(Link,0,sizeof(Link)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(stack,0,sizeof(stack)); memset(ins,false,sizeof(ins)); memset(bel,0,sizeof(bel)); memset(all,0,sizeof(all)); memset(Out_point,0,sizeof(Out_point)); cin>>n>>m; for(int i=1;i<=m;i++) { cin>>xx>>yy; init(xx,yy); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=len;i++) { if(bel[a[i].x]==bel[a[i].y]) continue; else if(bel[a[i].x]!=bel[a[i].y]) { Out_point[bel[a[i].x]]++; } } ans=n; for(int i=1;i<=tot;i++) { if(Out_point[i]==0) { if(GG==false) { GG=true; ans=all[i]; } else if(GG==true) { ans=0; break; } } } cout<<ans<<endl; return 0; }
|