1 条题解
-
0
自动搬运
来自洛谷,原作者为

zybnxy
**搬运于
2025-08-24 21:36:34,当前版本为作者最后更新于2019-01-30 12:55:44,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
标签:求强联通分量
何为强联通分量
有向图强连通分量:在有向图中,如果两个顶点间()有一条从到的有向路径,同时还有一条从到的有向路径,则称两个顶点强连通。如果有向图的每两个顶点都强连通,称是一个强连通图。有向图的极大强连通子图,称为强连通分量。 ——百度百科
事实上,你大概可以理解为:如果一个图的子图中,任意两点可以相互到达,那么这就组成了一个强联通分量。
如果还不理解怎么办?没关系,我们上图像来理解

如图,在这个有向图中,一共有三个强联通分量
如何求强联通分量
我们需要两个非常重要的数组,在这里先说明一下
,表示这个点在时是第几个被搜到的。
,表示这个点以及其子孙节点连的所有点中最小的值
,表示当前所有可能能构成是强连通分量的点。
,表示一个点是否在数组中。
我们使用的方法 (1)、首先初始化第几个被到
(2)、将存入中,并将设为
(3)、遍历的每一个能到的点,如果这个点为,即仍未访问过,那么就对点进行,然后
(4)、假设我们已经完了的所有的子树那么之后无论我们再怎么,点的值已经不会再变了。
至此,完美结束
那么如果这说明了什么呢?
再结合一下和的定义来看看吧
表示点被到的时间,表示和所有的子树所能到达的点中最小的。
这说明了点及点之下的所有子节点没有边是指向u的祖先的了,即我们之前说的点与它的子孙节点构成了一个最大的强连通图即强连通分量
此时我们得到了一个强连通分量,把所有的u点以后压入栈中的点和u点一并弹出,将它们的置为,如有需要也可以给它们打上相同标记(同一个数字)
可以理解,但为什么也要这么做呢?
因为low的定义如上,也就是说如果没有子孙与u的祖先相连的话,一定是它和它的所有子孙中最小的(因为它的所有子孙一定比他后搜到)。
有什么用?
如果在中,之后的所有点在被回溯到时和栈中所有在它之后的点都构成强连通分量。
有什么用?
应该能看出来吧,就是记录一个点它最大能连通到哪个祖先节点(当然包括自己)
如果遍历到的这个点已经被遍历到了,那么看它当前有没有在里,如果有那么
如果已经被弹掉了,说明无论如何这个点也不能与构成强连通分量,因为它不能到达
如果还在栈里,说明这个点肯定能到达,同样能到达他,他俩强联通。
接下来,就是非常简单的手过程了
从节点开始,把遍历到的节点加入栈中。搜索到节点时,,找到了一个强连通分量。退栈到为止,为一个强连通分量。

之后返回节点,发现,于是我们又找到了一个新的强联通分量

返回节点,继续搜索到节点,把加入堆栈。发现节点向节点有后向边,节点还在栈中,所以。节点已经出栈,是横叉边,返回,为树枝边,所以。

继续回到节点,最后访问节点。访问边,还在栈中,所以。返回后,发现,把栈中节点全部取出,组成一个连通分量。

至此,算法结束,我们找到了全部的个强联通分量
程序实现代码如下
inline int tarjan(int u) { low[u]=dfn[u]=++dfn_sum; stack[top++]=u; for(int i=head[u];i;i=e[i].next) { int v=e[i].to; if(dfn(v)) low[u]=min(low[u],dfn[v]); else { tarjan(v); low[u]=min(low[u],low[v]); } } if(low[u]==dfn[u]) { int now=stack[--top];s_sum++; s[u]+=s_sum; while(now!=u) { s[now]=s_num; now=s[--top]; } } }所以,我们再来分析一下这道题。
首先,不难发现,如果这所有的牛都存在同一个强联通分量里。那么它们一定互相受欢迎。
那么,我们怎么来找明星呢。
很简单,找出度为的强联通分量中的点。这样可以保证所有的人都喜欢它,但是它不喜欢任何人,所以说不存在还有人事明星。
此题还有一个特殊情况:
如果有两个点分别满足出度为零的条件,则没有明星,这样无法满足所有的牛喜欢他。
有了上边的解释,题目就不是那么难了,代码如下
#include<bits/stdc++.h> #define ri register int using namespace std; const int maxn=1e4+5; const int maxm=5e4+5; int to[maxm],nex[maxm],fir[maxn]; int col,num,dfn[maxn],low[maxn],de[maxn],si[maxn]; int tot=0,co[maxn],n,m; int top,st[maxn]; template<class T> inline void read(T &x) { x=0; register char c=getchar(); register bool f=0; while (!isdigit(c)) f ^=c=='-',c=getchar(); while (isdigit(c)) x=x*10+c-'0',c=getchar(); if(f)x=-x; } template <class T> inline void print(T x) { if(x<0)putchar('-'),x=-x; if(x>9)print(x/10); putchar('0'+x%10); } inline void ins(int x,int y) { to[++tot]=y; nex[tot]=fir[x]; fir[x]=tot; } void Tarjan(int u) { dfn[u]=low[u]=++num; st[++top]=u; for(int i=fir[u];i;i=nex[i]) { int v=to[i]; if(!dfn[v]) { Tarjan(v); low[u]=min(low[u],low[v]); } else if(!co[v])low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { co[u]=++col; ++si[col]; while(st[top]!=u) { ++si[col]; co[st[top]]=col; --top; } --top; } } int main() { int x,y; read(n);read(m); for(ri i=1;i<=m;i++) { read(x);read(y); ins(y,x); } for(ri i=1;i<=n;i++) if(!dfn[i])Tarjan(i); for(ri i=1;i<=n;i++) for(ri j=fir[i];j;j=nex[j]) if(co[i]!=co[to[j]])de[co[to[j]]]++; int ans=0,u=0; for(ri i=1;i<=col;i++)if(!de[i])ans=si[i],u++; if(u==1)print(ans); else print(0); return 0; }
- 1
信息
- ID
- 1326
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者