首页 > 其他分享 >强连通分量

强连通分量

时间:2023-09-11 20:14:49浏览次数:38  
标签:连通 int text ++ dfn low 分量

强连通图判定

从一个点出发,可以遍历整张图,再将所有的边反向,从同一点出发,可以遍历整张图,则该图是强连通图

Tarjan求有向图强连通分量

\(\text{dfn[u]}\) 表示点 \(u\) 的dfs序,\(\text{low[u]}\) 表示点 \(u\) 可以走到的dfs序最小的点

我们在dfs树上,当一个点的 \(\text{low[u]}=\text{dfn[u]}\) ,即它无法进一步向上走时,我们就找到了一个强连通分量

Code:

int dfn[N],low[N],ins[N],stk[N],top,ts,tot,scc[N];
inline void tarjan(int u)
{
	dfn[u]=low[u]=++ts;
	ins[u]=1;
	stk[++top]=u;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(ins[v]) low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==low[v])
	{
		int x;++tot;
		do{
			x=stk[top--];
			scc[x]=tot;
			ins[x]=0;
		}while(x!=u);
	}
}

Trick:出栈顺序为拓扑序的倒序

Kosaraju算法

算法流程

先将图 \(G\) 所有的边反转得到图 \(G'\) ,计算 \(G'\) 的拓扑序并按照 \(G'\) 的拓扑序对 \(G\) 进行深度优先搜索

证明

对于两个点 \(x,y\) ,如果在图 \(G\) 中存在 \(x\rightarrow y\) ,拓扑序中 \(x\) 在 \(y\) 前面,即 \(G'\) 中存在 \(x\rightarrow y\) ,即 \(G\) 中存在 \(y\rightarrow x\) ,因此 \(x,y\) 在同一强连通分量中

HAOI2006 受欢迎的牛

题面

给定 \(n\) 个点 \(m\) 条边的有向图,求出能够被所有点到达的点

\(n\le 1\times 10^4,m\le 5\times 10^5\)

题解

求强连通分量,缩点。如果只有一个出度为 \(0\) 的点,那么这个点就是答案;如果这个数量大于等于 \(2\) ,那么无解

ZJOI2007 最大半连通子图

题面

有一个 \(n\) 个点 \(m\) 条边的有向图,你要找一个点集 \(S\),使得 \(u,v\) 在 \(S\) 中有 \(u\) 能到达 \(v\) 或者 \(v\) 能到达 \(u\)
求出最大的 \(S\),然后求有多少种选法

\(n\le 10^5,m\le 10^6\)

题解

缩点,要求的最大半联通子图一定是DAG上的一条链,分别记 \(f[i],g[i]\) 表示大小和方案数,dp即可

割点

定义

无向图中,删掉该点后使得图不连通,则该点为割点

求法

对于一个点 \(u\) 和它的一个儿子 \(v\)

  1. 如果该点是根,那么儿子数 \(\ge 2\) 就是割点
  2. 如果该点不是根,那么当 \(\text{low[v]}>=\text{dfn[u]}\) 时该点就是割点

Code:

int dfn[maxn],low[maxn],ts,rt;
std::vector<int> cut;
void tarjan(int u){
	dfn[u]=low[u]=++ts;
	int son=0;
	for(int i=head[u];~i;i=edge[i].next){
		int v=edge[i].v;
		if(!dfn[v]){
			++son;
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if((u==rt&&son>1)||(u!=rt&&low[v]>=dfn[u]))
				cut.push_back(u);
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

定义

无向图中,删掉一条边使得图不连通,这条边就是桥

求法

对于一条边 \(u\rightarrow v\),如果 \(\text{low[v]}>\text{dfn[u]}\) ,则该边就是桥

Code:

int dfn[maxn],low[maxn],ts,rt;
std::vector<pii> bridge;
void tarjan(int u){
	dfn[u]=low[u]=++ts;
	for(int i=head[u];~i;i=edge[i].next){
		int v=edge[i].v;
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u])
				bridge.push_back(mkp(u,v));
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

变双联通分量/无向图缩点

边双连通分量

删掉该点集中任意一条边都不会影响该点集的连通性,则称该点集为一个边双强连通分量

Code:

inline void tarjan(int u,int fa)  
{  
    low[u]=dfn[u]=++ts;  
    ins[u]=1;  
    stk[++top]=u;  
    for(int i=head[u];i;i=edge[i].nxt)  
    {  
        int v=edge[i].v;  
        if(v==fa) continue;  
        if(!dfn[v])         {  
            tarjan(v,u);  
            low[u]=min(low[u],low[v]);  
        }  
        else low[u]=min(low[u],dfn[v]);  
    }  
    if(low[u]==dfn[u])  
    {  
        int x;++tot;  
        do{  
            x=stk[top--];  
            ins[x]=0;  
            dcc[x]=tot;  
        }while(x!=u);  
    }  
}

DFS树求割点/桥

对于每一条非树边 \((u,v)\) ,让 \(u+1\) ,让 \(v-1\) (注意这里一定是返租边),那么就可以求出每一边/点的覆盖次数,覆盖次数为 \(0\) 的就是割点/桥

动态加边维护桥

上面那个做法可以在线完成

可以使用一个并查集维护当前双联通分量中的点,记录一下每个双联通分量中最高的点。

然后对于一条非树边,暴力将这些点合并起来即可

因为一条边最多被合并一次,需要不超过 \(\mathcal O(m)\) 次的并查集操作

点双联通分量/圆方树

点双连通分量

删掉点集中的任意一个点,该点集仍然连通,则称该点集是一个点双连通分量

圆方树

原图中所有的点作为圆点,对于每一个点双,我们建立一个方点,让这个方点连接点双中的每一个点,那么所有的度数大于 \(1\) 的圆点都是割点

实际题目中,可以给圆方树的圆点/方点赋上不同的权值,再通过dp、树剖等方式来计算答案

Code:

int dfn[N],low[N],ts,tot,stk[N],top,val[N],sum;
vector<int> G[N];
inline void tarjan(int u)
{
	++sum;
	dfn[u]=low[u]=++ts;
	stk[++top]=u;
	val[u]=-1;
	for(int i=head[u];i;i=edge[i].nxt)
	{
		int v=edge[i].v;
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u])
			{
				int x;++tot;
				do{
					x=stk[top--];
					++val[tot];
					G[x].emplace_back(tot);
					G[tot].emplace_back(x);
				}while(x!=v);
				++val[tot];
				G[u].emplace_back(tot);
				G[tot].emplace_back(u);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}

小Trick

给你一个图,每条边都只在一个环内,等价于每个点双联通分量是一条边或者一个环

POJ 3177 Redundant Paths

题面

给定 \(n\) 个点 \(m\) 条边的无向图,求至少需要加多少条边使得整张图成为边双联通分量

\(n\le 1\times 10^4,m\le 5\times 10^4\)

题解

缩点,以任意一个度数大于 \(1\) 的点为根,记叶子数为 \(l\) ,答案即为 \(\lceil \frac{l}2 \rceil\)

POI2008 BLO-Blockade

题面

给定 \(n\) 个点 \(m\) 条边的无向图,求将与每个点四周的边去掉之后有多少对点不连通 (\((x,y)\),\((y,x)\) 算两种情况)

\(n\le 1\times 10^5,m\le 5\times 10^5\)

题解

对于每一个点 \(i\)

  1. 如果 \(i\) 不是割点,那么就会产生以 \(2(n-1)\) 对与 \(i\) 有关的不连通点对
  2. 如果 \(i\) 是割点,设删掉与 \(i\) 有关的边之后产生了 \(k\) 个连通块,那么答案就为

    \[2(n-1)+\sum_{i=1}^k\sum_{j=1,j\neq i}^k\text{siz}[i]\times \text{siz} [j] \]

    考虑优化,我们有

    \[\sum_{j=1,j\neq i}^k\text{siz}[j]=n-\text{siz}[i]-1 \]

计算答案即可

HNOI2012 矿场搭建

题面

给定 \(n\) 个点 \(m\) 条边的无向图,要求从中选出最少的关键点,使得无论删除哪个点,其余的点都存在通往关键点的路径,求出关键点数量和方案数

\(n\le 500,m\le 1000\)

题解

建立圆方树,对于所有叶节点,它们不是割点,不会影响连通性,如果该点为割点那么设 \(f[u]\) 表示删掉点 \(u\) 时其子树内最少的关键点数量,\(g[u]\) 表示方案数,分圆方两种情况讨论

  1. 该点是圆点,那么有

    \[\begin{cases}f[u]=\sum_{v\in \text{son}_u}\max(f[v],1)\\g[u]=\sum_{v\in\text{son}_u}g[v]\end{cases} \]

    其中当 \(f[v]=0\) 时,\(g[v]=\text{siz}[v]\)
  2. 该点是方点,那么有

    \[\begin{cases}f[u]=\sum_{v\in \text{son}_u}f[v])\\g[u]=\sum_{v\in\text{son}_u}g[v]\end{cases} \]

P4630 [APIO2018] 铁人两项 (圆方树上dp)

题目

题解

考虑到当我们固定\(s,f\)时,我们\(c\)的选择就是\(s\)到\(f\)所有简单路径的并,然后减去\(2\)(因为\(c\)不可以选在\(s,f\)处)

进一步考虑,\(s\)到\(f\)简单路径的并就是路径上点双大小的并

所以建出原图的圆方树,将方点的权值设为这个点双中圆点的个数,将圆点的权值设为\(-1\),那么从\(s\)到\(f\)简单路径的并就是圆方树上\(s\)到\(f\)的路径

我们设 \(f_i\) 表示 \(s,f\) 在 \(i\) 子树中的方案数,考虑枚举中转点 \(c\),分两种情况转移

  • 点 \(c\) 是圆点时,假设它有 \(4\) 棵子树,它的子树对答案的贡献是

\[S_1S_2+S_1S_3+S_1S_4+S_2S_3+S_2S_4+S_3S_4 \]

看上去这个转移是 \(\mathcal O(n^2)\) 的,是我们可以这么做

\[S_1S_2+(S_1+S_2)S_3+(S_1+S_2+S_3)S_4 \]

所以我们每次记一个前缀子树和,然后乘上下一个子树的大小进行转移,这是\(\mathcal O(n)\)的

  • 当\(c\)是方点,如果\(s,f\)不在这个点双内,那么其贡献一定被这个点双中的圆点中就已经统计过了,所以考虑如何计算\(s,f\)在点双内的方案数
  1. 如果 \(s,f\) 中只有一个在点双中,那么 \(c\) 可以选的位置就有 \(deg-1\) 个,总共有 \(deg\times (deg-1)\) 次贡献,但是当 \(f\) 选在了割点处,那么\(c\)就无处可选了,乘的另外一部分是相同的,所以整个就少选了\(deg\) 次,所以总次数就变成了 \(deg\times (deg-2)\)
  2. 如果 \(s,f\) 都在点双内部,显然可以选择的位置就有 \(deg-2\) 个

所以在上面圆点的转移乘上 \(deg-2\) 的系数就行了

标签:连通,int,text,++,dfn,low,分量
From: https://www.cnblogs.com/starroadxyz/p/17694347.html

相关文章

  • tarjan强连通分量
    intscc[N],sc;//结点i所在scc的编号intsz[N]; //强连通i的大小//dfn(u)为搜到结点u时的次序编号//low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号//当dfn(u)=low(u)时,以u为根的搜索子树上的所有节点是一个强连通分量voidtarjan(intu){ dfn[u]=low[u]......
  • 华为SAN存储在Red Hat系统下的主机连通性FAQ
    1、建立iSCSI连接后,主机系统无法重启现象主机系统和存储系统建立iSCSI连接后,主机系统重启失败。根因分析主机停止iSCSI服务时,session没有关掉。解决方案主机系统重启前,请先停止iSCSI服务,然后再重启主机。2、替换LUN后无法更新LUN的信息现象当替换LUN的时候(前后两个LUN使......
  • 《北文的树形连通块dp》
    想看原文可以看这个对于一些问题,让我们数颜色数,要知道数颜色数这个东西非常的不好维护。往往我们四种解决方法:直接暴力数只数最后一个出现的(如果有什么性质的话)容斥,减去算重的将每个分开来计算贡献本文着重讲解第三种和第四种。......
  • 图解Spark Graphx基于connectedComponents函数实现连通图底层原理
    原创/朱季谦第一次写这么长的graphx源码解读,还是比较晦涩,有较多不足之处,争取改进。一、连通图说明连通图是指图中的任意两个顶点之间都存在路径相连而组成的一个子图。用一个图来说明,例如,下面这个叫graph的大图里,存在两个连通图。左边是一个连接图,该子图里每个顶点都存在路......
  • tarjan求点双连通分量
    边双连通分量见tarjan求边双连通分量部分参考lyd《算法竞赛进阶指南》前置知识给定无向连通图\(G=(V,E)\)割点:若对于\(x\inV\),从图中删去x及其连边,\(G\)分裂成两个及以上不相连子图,那么x是\(G\)的割点时间戳:在深度优先访问时按照每个节点第一次被访问的时间顺......
  • tarjan求边双连通分量
    本文仅为作者的一些学习笔记,内容可能具有局限性,比如并未就“点双连通分量”进行整理。部分参考lyd《算法竞赛进阶指南》前置概念桥(割边):若\(e\inE\),如果删去e后图分裂成两个子图,那么e这条边就为桥(割边)。时间戳:在深度优先访问时按照每个节点第一次被访问的时间顺序,依次......
  • Tarjan 求强连通分量
    欢迎批评指正!前置芝士什么是强连通分量(\(\text{SCC}\))?强连通分量,一般指有向图的极大强连通子图,在这些子图中,所有点双向可达。dfs序:即dfs过程中访问点的顺序。dfs生成树:由dfs过程中访问的边组成的边集和原图的点集组成的树。树边,非树边:属于dfs过程中访问的边为......
  • hdu:Oil Deposits(dfs连通图)
    ProblemDescriptionTheGeoSurvCompgeologicsurveycompanyisresponsiblefordetectingundergroundoildeposits.GeoSurvCompworkswithonelargerectangularregionoflandatatime,andcreatesagridthatdividesthelandintonumeroussquareplots.......
  • 如何优雅的使用telnet测试端口连通性
    telnet命令是TELNET协议的用户接口,它支持两种模式:命令模式和会话模式,虽然telnet支持许多命令,但大部分情况下,我们只是使用它查看目标主机是否打开了某端口(默认是23)。其执行结果有两种:端口未打开$telnet101.199.97.6562715Trying101.199.97.65...telnet:connecttoaddres......
  • 强连通分量
    目录强连通分量dfs森林强连通分量dfs森林树边(treeedge)返祖边(backedge)......