洛谷 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 }
请各位大佬斧正(反正我不认识斧正是什么意思)