受欢迎的牛

受欢迎的牛 jdoj-neooj-1076

    题目大意:给你n个点,m条单项边,求可以和所有点联通的点的个数。

    注释:n<=10,000,m<=50,000

      想法:这题也是一道tarjan裸题,让我来A掉吧!这题和爱在心中(爱在心中?猛戳)类似,只不过这题有坑... ...用panxf的模板A不掉这道题... ...为什么呢?因为n的数据范围,会MLE。但是我们发现,这道题只需要求出强连通分量之中点的个数。所以,我们可以将原本记录强连通分量的点的数组f压成一维。但是,我们不可以用邻接矩阵来存边了。同样的,我们用链式前向星,保证不会MLE。而且,我们在这里需要作出一个特判(不然会WA掉),就是如果存在一个点,这个点入度和出度都为0,最后的答案一定为0,这是显然的。下面,我们想怎样才能正确的求出个数。同样的,我们枚举出度为0的强连通分量,如果个数大于1,显然个数为0。否则,就是这个强联通分量的个数。

    最后,附上丑陋的代码... ...

 1 #include <iostream>
 2 #include <cstdio>
 3 #define N 10010
 4 #define M 50010
 5 using namespace std;
 6 int x[M],y[M];
 7 int to[M],nxt[M],head[N],mid;
 8 int z[N],deep[N],low[N],f[N];
 9 int inwhat[N],chu[N];
10 int tot,top,ans;
11 bool v[N],isv1[N],isv2[N];
12 int inz[N];
13 void add(int a,int b)//存边 
14 {
15     to[++mid]=b;
16     nxt[mid]=head[a];
17     head[a]=mid;
18 }
19 int n;
20 void tarjan(int x)//tarjan 
21 {
22     z[++top]=x;
23     v[x]=inz[x]=1;
24     low[x]=deep[x]=++tot;
25     for(int i=head[x];i;i=nxt[i])//枚举链式前向星 
26     {
27         if(!v[to[i]]) tarjan(to[i]),low[x]=min(low[x],low[to[i]]);
28         else if(inz[to[i]]) low[x]=min(low[x],deep[to[i]]);
29     }
30     if(deep[x]==low[x])
31     {
32         int t;
33         ans++;
34         do
35         {
36             t=z[top--];inz[t]=0;
37             f[ans]++;//这里不用记录强连通分量里具体的点,只需要个数即可。 
38             inwhat[t]=ans;
39         }while(t!=x);
40     }
41 }
42 int main()
43 {
44     int m;
45     scanf("%d%d",&n,&m);
46     int a,b;
47     for(int i=1;i<=m;i++)
48     {
49         scanf("%d%d",&x[i],&y[i]);
50         isv1[x[i]]=1;//出度 
51         isv2[y[i]]=1;//入度 
52         add(x[i],y[i]);
53     }
54     for(int i=1;i<=n;i++)
55     {
56         if(isv1[i]&&!v[i]) tarjan(i);//枚举整张图 
57     }
58     if(n>=2)//特判 
59     {
60         for(int i=1;i<=n;i++) if(isv1[i]==0&&isv2[i]==0)
61         {
62             printf("0\n");
63             return 0;
64         }
65     }
66     for(int i=1;i<=m;i++)
67     {
68         if(inwhat[x[i]]!=inwhat[y[i]]) chu[inwhat[x[i]]]++;
69     }
70     int cnt=0;
71     int middle=0;
72     for(int i=1;i<=ans;++i) if(chu[i]==0) cnt++,middle=i;
73     if(cnt>1)
74     {
75         printf("0\n");
76         return 0;
77     }
78     else printf("%d\n",f[middle]);//输出即可。 
79     return 0;
80 }

    小结:tarjan是一个O(n)的算法,极其经典,正在getting中。

      错误:在记录出度的时候,不是chu[x[i]]++,而是chu[inwhat[x[i]]]++。

posted @ 2018-01-18 22:00  JZYshuraK_彧  阅读(350)  评论(0编辑  收藏  举报