图的连通性

边双连通缩点

模板题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
const int N=2e6+5;
int n,m,u,v,_=1,tot,top,col;
int head[N],dfn[N],low[N],id[N],sta[N];
vector<int> ans[N];
inline int read()
{
	int s=0,t=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s*t;
}
struct node
{
	int to,next;
}e[N<<1];
inline void add(int u,int v)
{
	++_;
	e[_].to=v;
	e[_].next=head[u];
	head[u]=_;
	return;
}
inline void tarjan(int u)
{
	dfn[u]=low[u]=++tot;
	sta[++top]=u;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to;
		if(i==id[u]) continue;
		if(!dfn[v])
		{
			id[v]=i^1;
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v])
			{
				++col;
				for(int cur=0;cur!=v;) ans[col].push_back(cur=sta[top--]);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
	return;
}
int main()
{
	n=read();
	m=read();
	while(m--)
	{
		u=read();
		v=read();
		add(u,v);
		add(v,u);
	}
	for(int i=1;i<=n;++i)
		if(!dfn[i])
		{
			tarjan(i);
			++col;
			for(int cur=0;cur!=i;) ans[col].push_back(cur=sta[top--]);
		}
	printf("%d\n",col);
	for(int i=1;i<=col;++i)
	{
		int sz=ans[i].size();
		printf("%d ",sz);
		for(int j=0;j<sz;++j) printf("%d%c",ans[i][j]," \n"[j==sz-1]);
	}
	return 0;
}

点双连通缩点

模板题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#define mp make_pair
using namespace std;
const int N=2e6+5;
int n,m,u,v,cnt,tot,top,col;
int head[N],dfn[N],low[N],sta[N],bel[N];
bool vis[N];
vector<int> pos[N];
inline int read()
{
	int s=0,t=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')t=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s*t;
}
struct node
{
	int to,next;
}e[N<<1];
inline void add(int u,int v)
{
	++cnt;
	e[cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
	return;
}
inline void tarjan(int u)
{
	dfn[u]=low[u]=++tot;
	sta[++top]=u;
	vis[u]=1;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to;
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<=low[v])
			{
				++col;
				for(int cur=0;cur!=v;)
				{
					bel[cur=sta[top--]]=col;
					pos[col].push_back(cur);
					vis[cur]=0;
				}
				bel[u]=col;
				pos[col].push_back(u);
			}
		}
		else if(vis[v]) low[u]=min(low[u],dfn[v]);
	}
}
int main()
{
	n=read();
	m=read();
	while(m--)
	{
		u=read();
		v=read();
		add(u,v);
		add(v,u);
	}
	for(int i=1;i<=n;++i)
		if(!dfn[i])
		{
			sta[top=0]=0;
			tarjan(i);
			if(!bel[i])
			{
				++col;
				for(int cur=0;cur!=i;)
				{
					bel[cur=sta[top--]]=col;
					pos[col].push_back(cur);
					vis[cur]=0;
				}
			}
		}
	printf("%d\n",col);
	for(int i=1;i<=col;++i)
	{
		printf("%d ",(int)pos[i].size());
		int sz=pos[i].size();
		for(int j=0;j<sz;++j) printf("%d%c",pos[i][j]," \n"[j==sz-1]);
	}
	return 0;
}

一些性质

  1. 强连通缩点后的编号是逆拓扑序。
  2. 强连通缩点后的图是 DAG;双连通缩点的图是树,所以无向图可以考虑在 dfs 树上解决。
  3. 保证强连通缩点后的结果相同的边是在 O ( n ) O(n) O(n) 级别的(“保证强连通缩点后的结果相同的边”是指改变 l o w [ ] low[] low[] 的边)。
  4. 将一个边双连通分量中的所有边定向后,一定是一个强连通分量。

dfs 树

1. ARC143D Bridges

题目

无向图强连通

1. CF1361E James and the Chase

题目

2. ARC092F Two Faced Edges

题目

点双连通

1. CF521E Cycling City

题目
首先,两点之间有 3 3 3 条不相交的简单路径的条件严格强于点双的 2 2 2 条。所以满足条件的 u u u, v v v 两点一定在同一个点双中。
首先可以注意到仅含有一条环的点双是不存在符合条件的点对的,进一步发现除了这种情况外的点双都含有符合条件。因为图中一定存在一个度 ≥ 3 \ge3 3 的点,假设这个点是 u u u。任选两个与 u u u 之间有相连的边的点,假设是 v 1 v_1 v1, v 2 v_2 v2。可以证明,如果删去边 u ↔ v 1 u\leftrightarrow v_1 uv1 u ↔ v 2 u\leftrightarrow v_2 uv2 后的 dfs 树上 l c a ( v 1 , v 2 ) lca(v_1,v_2) lca(v1,v2) 不是 u u u。因为如果出现这种情况 u u u 为割点。
假设 l c a ( v 1 , v 2 ) = w lca(v_1,v_2)=w lca(v1,v2)=w,那么 3 3 3 条路径分别是 u → w u\rightarrow w uw u → v 1 → ⋯ → w u\rightarrow v_1\rightarrow\cdots\rightarrow w uv1w u → v 2 → ⋯ → w u\rightarrow v_2\rightarrow\cdots\rightarrow w uv2w

边双连通

1.CF555E Case of Computer Network

题目
首先有一个性质:

将一个边双连通分量中的所有边定向,一定是一个强连通分量。

那么先边双缩点,如果有向点对 ( s , t ) (s,t) (s,t) s s s t t t 属于同一个边双,那么一定可行。
考虑 s s s t t t 不在同一个边双的情况。边双缩点后的图为树。考虑树上差分,记 t a g [ i ] [ 0 / 1 ] tag[i][0/1] tag[i][0/1] 表示 f a t h e r i → i father_i\rightarrow i fatherii 的边是否被要求为向上的边 / / / 向下的边。
如果同时 t a g [ i ] [ 0 ] ≠ 0 tag[i][0]\not=0 tag[i][0]=0, t a g [ i ] [ 1 ] ≠ 0 tag[i][1]\not=0 tag[i][1]=0,那么不合法,否则合法。注意特判 s s s, t t t 不连通的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值