洛谷 P2341 [HAOI2006]受欢迎的牛 题解

今天学了强连通分量的Tarjan算法,做了这道类似于板子题的题(尽管我调了1.5h)。主要的思路是用Tarjan缩点之后,求每个点的入度(实际上是出度,因为我是反着连边的)。如果

有且只有一个点的入度大于零,那个强连通分量里有的点的个数就是答案。具体实现见代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 10010
 6 #define maxm 50010
 7 using namespace std;
 8 int dfn[maxn],low[maxn],st[maxn],inn[maxn],head[maxm];
 9 int de[maxn],si[maxn];
10 int n,m,cnt,top,inl,num;
11 struct node
12 {
13     int u,v,nex;
14 }edge[maxm];
15 inline int read() 
16 {
17     int x=0;
18     bool f=1;
19     char c=getchar();
20     for(; !isdigit(c); c=getchar()) if(c=='-') f=0;
21     for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+c-'0';
22     if(f) return x;
23     return 0-x;
24 }
25 inline void write(int x)
26 {
27     if(x<0){putchar('-');x=-x;}
28     if(x>9)write(x/10);
29     putchar(x%10+'0');
30 }
31 inline void add(int x,int y)
32 {
33     cnt++;
34     edge[cnt].u=x;
35     edge[cnt].v=y;
36     edge[cnt].nex=head[x];
37     head[x]=cnt;
38 }
39 inline void Tarjan(int from)//用Tarjan缩点 
40 {
41     dfn[from]=low[from]=++num;
42     st[++top]=from;
43     for(int i=head[from];i!=-1;i=edge[i].nex)
44     {
45         int to=edge[i].v;
46         if(!dfn[to])
47         {
48             Tarjan(to);
49             low[from]=min(low[from],low[to]);
50         }
51         else if(!inn[to])
52             low[from]=min(low[from],dfn[to]);
53     }
54     if(low[from]==dfn[from])
55     {
56         inn[from]=++inl;
57         ++si[inl];//记录每个强连通分量里有多少个点 
58         while(st[top]!=from)
59         {
60             ++si[inl];//同上 
61             inn[st[top]]=inl;
62             --top;
63         }
64         --top;
65     }
66 }
67 int main()
68 {
69     memset(head,-1,sizeof(head));
70     n=read();m=read();
71     for(int i=1;i<=m;i++)
72     {
73         int x,y;
74         x=read();y=read();
75         add(y,x);//反着连边,这样就可以求入度而不是出度了 
76     }
77     for(int i=1;i<=n;i++)
78         if(!dfn[i])
79             Tarjan(i);//缩点 
80     for(int i=1;i<=n;i++)
81        for(int j=head[i];j!=-1;j=edge[j].nex)
82            if(inn[i]!=inn[edge[j].v]) de[inn[edge[j].v]]++;//更新入度 
83     int ans=0,u=0;
84     for(int i=1;i<=inl;i++)
85     {
86         if(!de[i])//如果入度大于零 
87         {
88             ans=si[i];//赋值 
89             u++;//记录有几个点符合条件 
90         }
91     }
92     if(u==1)//判断是否有且仅有一个入度大于零的点 
93         write(ans);
94     else 
95         write(0);//否则输出无解 
96     return 0;
97 }
请各位大佬斧正(反正我不认识斧正是什么意思)

 

posted @ 2019-07-24 14:18  handsome_zyc  阅读(213)  评论(0编辑  收藏  举报