点双连通分量

摘要:
对于一个无向图,如果它没有割点,则称为“点双连通图”。无向图的最大点双连通子图称为“点双连通分量”。它能做什么?如果无向图中的所有点都是双连通的,我们可以得到一棵树。我们可以很容易地将一些路径问题转化为树问题吗?
它是什么?

对于一个无向图,如果它没有割点,则称其为“点双联通图”

无向图的极大点双连通子图成为“点双连通分量”

它可以用来做什么?

如果对无向图中的所有点双连通分量缩点,可以得到一颗树,可以比较方便地将一些路径问题转化为树上问题

怎么求?

我们可以在(Tarjan)求割点时,顺便把所有(v-DCC)求出来,流程如下:

  1. 当一个节点第一次被访问时,入栈
  2. (dfn[x]leq low[v])成立(即割点判定法则)时
    从弹栈,直至节点(v)被弹出,所有弹出的节点与(x)共同构成一个(v-DCC)

缩点相对复杂一些这就是我一直不会写点双的原因其实好像也不是很难,因为一个割点可能属于多个(v-DCC)

设图中一共有(k)个割点,(t)(v-DCC),建立一张包含(k+t)个节点的新图,把每个(v-DCC)和割点作为新图中的节点,并在在每个割点和所有包含它的(v-DCC)之间连边

最终获得的图即是一颗树(或是森林)


例题

BZOJ3331 压力

TLDR一句话题意:
给定(n)个点(m)条边的无向连通图,以及(q)个点对
对于每个点对,它们的路径上有一些点是必经点(包括起点和终点)
对于图上的每个点,求出它们是多少个点对路径的必经点

Description

如今,路由器和交换机构建起了互联网的骨架。处在互联网的骨干位置的
核心路由器典型的要处理(100Gbit/s)的网络流量。他们每天都生活在巨大的压力
之下。
小强建立了一个模型。这世界上有(N)个网络设备,他们之间有M个双向的
链接。这个世界是连通的。在一段时间里,有(Q)个数据包要从一个网络设备发
送到另一个网络设备。
一个网络设备承受的压力有多大呢?很显然,这取决于(Q)个数据包各自走
的路径。不过,某些数据包无论走什么路径都不可避免的要通过某些网络设
备。
你要计算:对每个网络设备,必须通过(包括起点、终点)他的数据包有
多少个?

Input

第一行包含3个由空格隔开的正整数N,M,Q。
接下来M行,每行两个整数u,v,表示第u个网络设备(从1开始编号)和
第v个网络设备之间有一个链接。u不会等于v。两个网络设备之间可能有多个
链接。
接下来Q行,每行两个整数p,q,表示第p个网络设备向第q个网络设备发
送了一个数据包。p不会等于q。

Output

输出N行,每行1个整数,表示必须通过某个网络设备的数据包的数量。

Sample Input

4 4 2
1 2
1 3
2 3
1 4
4 2
4 3

Sample Output

2
1
1
2

HINT

【样例解释】
设备(1,2,3)之间两两有链接,(4)只和(1)有链接。(4)想向(2)(3)各发送一个数据包。显然,这两个数据包必须要经过它的起点、终点和(1)

【数据规模和约定】
对于(40\%)的数据,(N,M,Q≤2000)
对于(60\%)的数据,(N,M,Q≤40000)
对于(100\%)的数据,(N≤100000,M,Q≤200000)

Solution

显然对于原图的一个点,如果它不是割点,那么除了作为起点和和终点,它不会成为任何一条路径的必经点,否则它一定是经过它的路径的必经点

对原图求点双并进行缩点,获得一棵树,新图中的点只有割点和各个点双
对于非割点,统计它是多少条路径的起点和终点
对于割点,计算它被多少条路径覆盖,LCA求路径,树上差分即可
这样,我们就成功地把原问题转化为树上问题

Code

#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#define maxn 200005//不知道为什么,如果只开100000在BZOJ上会REemmmmm
#define maxm 200005
using namespace std;
typedef long long ll;

int n,m,q;
int ans[maxn],c[maxn];

struct edge
{
	int u,v,nxt;
}g[maxm*2];

int nn;//强连通分量编号
vector<int> dcc[maxn];//使用vector记录每个v-DCC所包含的节点

int head[maxn],ecnt;
void eADD(int u,int v)
{
	g[++ecnt].u=u;
	g[ecnt].v=v;
	g[ecnt].nxt=head[u];
	head[u]=ecnt;
}

int dfn[maxn],low[maxn],sign;
int stk[maxn],top;
int bel[maxn],cut[maxn];
int root;
void Tarjan(int u,int fa)
{
	dfn[u]=low[u]=++sign;
    if(u==root && head[u]==0)
    {
    	dcc[++nn].push_back(u);
        return;
    }//孤立点
	stk[++top]=u;
	int flag=0;
	for(register int i=head[u];i;i=g[i].nxt)
	{
		int v=g[i].v;
		if(v==fa)
			continue;
		if(!dfn[v])
		{
			Tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u])
			{
				++flag;
				if(u!=root || flag>1)
					cut[u]=1;
				++nn;
				while(stk[top+1]!=v)
					dcc[nn].push_back(stk[top--]);
				dcc[nn].push_back(u);
			}
		}
		else
			low[u]=min(low[u],dfn[v]);
	}
}

int prt[maxn][21],dep[maxn];
void dfs0(int u,int fa,int depth)
{
	prt[u][0]=fa;
	dep[u]=depth;
	for(register int i=1;(1<<i)<=depth;++i)
		prt[u][i]=prt[prt[u][i-1]][i-1];
	for(register int i=head[u];i;i=g[i].nxt)
	{
		int v=g[i].v;
		if(v!=fa)
			dfs0(v,u,depth+1);
	}
}

int LCA(int x,int y)
{
	if(dep[x]<dep[y])
		swap(x,y);
	for(register int i=20;i>=0;--i)
		if(dep[prt[x][i]]>=dep[y])
			x=prt[x][i];
	if(x==y)
		return x;
	for(register int i=20;i>=0;--i)
		if(prt[x][i]!=prt[y][i])
		{
			x=prt[x][i];
			y=prt[y][i];
		}
	return prt[x][0];
}

void dfs1(int u,int fa)
{
	for(register int i=head[u];i;i=g[i].nxt)
	{
		int v=g[i].v;
		if(v!=fa)
		{
			dfs1(v,u);
			c[u]+=c[v];
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(register int i=1;i<=m;++i)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		eADD(u,v),eADD(v,u);
	}
	for(register int i=1;i<=n;++i)
		if(!dfn[i])
			Tarjan(root=i,0);
	int new_id=nn;
	for(register int i=1;i<=n;++i)
		if(cut[i])
			bel[i]=++new_id;//为每个割点分配单独的bel
	memset(g,0,sizeof(g));
	memset(head,0,sizeof(head));
	ecnt=0;//清空原图,建立新图
	for(register int i=1;i<=nn;++i)
		for(register int j=0;j<dcc[i].size();++j)
			if(cut[dcc[i][j]])
				eADD(bel[dcc[i][j]],i),eADD(i,bel[dcc[i][j]]);
			else
				bel[dcc[i][j]]=i;//非割点只会属于一个点双
	dfs0(1,0,1);
	while(q--)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		if(bel[u]==bel[v])//特判
		{
			++ans[u],++ans[v];
			continue;
		}
		int lca=LCA(bel[u],bel[v]);
		++c[bel[u]],++c[bel[v]];
		if(!cut[u])++ans[u];//若为割点,则不需要单独增加答案
		if(!cut[v])++ans[v];
		c[lca]--,c[prt[lca][0]]--;//差分
	}
	dfs1(1,0);
	for(register int i=1;i<=n;++i)
	{
		if(cut[i])
			printf("%d
",c[bel[i]]+ans[i]);
		else
			printf("%d
",ans[i]);
	}
	return 0;
}

免责声明:文章转载自《点双连通分量》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇CVAT 用户指南FPGA中的INOUT接口和高阻态下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

使用WinPcap编程(1)——获取网络设备信息

pcap_if_t是一个interface数据结构,表明网络接口的信息。网络接口就是interface,就是我们用来上网的设备,一般为网卡,还有一些虚拟网卡也算作这样的接口。它的结构如下: struct pcap_if { struct pcap_if *next; char *name; char *descr...

数据中心网络架构的问题与演进 — SDN

目录 文章目录 目录 前文列表 OpenFlow 源起 从 OpenFlow 衍生 SDN 前文列表 《数据中心网络架构的问题与演进 — 传统路由交换技术与三层网络架构》 《数据中心网络架构的问题与演进 — 网络虚拟化》 《数据中心网络架构的问题与演进 — CLOS 网络与 Fat-Tree、Spine-Leaf 架构》 《数据中心网络架构的问...

网络设备性能指标之pps

基本概念: Bps:Byte per second 每秒传输多少字节 bps: bits per second 每秒传输多少位 ,这个也叫做端口速率 pps:Packet Per Second(包每秒),网络设备的转发性能以“包转发性能”来表示,即设备在单位时间内能够处理多少个“包”,这决定了设备转发能力的强弱。 mbps:Million bits per...

QNX 实时操作系统(Quick Unix)

Gordon Bell和Dan Dodge在1980年成立了Quantum Software Systems公司,他们根据大学时代的一些设想写出了一个能在IBM PC上运行的名叫QUNIX(Quick UNIX)的系统,直到AT&T发律师函过来才把名字改成QNX。 中文名 QNX 实时操作系统 POSⅨ 规范 系统 嵌入式系统 目录...

点双连通分量与割点

前言 在图论中,除了在有向图中的强连通分量,在无向图中还有一类双连通分量 双连通分量一般是指点双连通分量 当然,还有一种叫做边双连通分量 点双连通分量 对于一个连通图,如果任意两点至少存在两条“点不重复”的路径,则说图是点双连通的(即任意两条边都在一个简单环中),点双连通的极大子图称为点双连通分量。 计算方法比较简单 在tarjan的过程中,如果由(i)...

linux sdio wifi驱动知识总结(一)

这两周在tq imx6ul下调一个迈威88w8801sdio wifi模组,最后尴尬的发现tq imx6ul并不支持sdio wifi。至于不支持的原因会在后面简单说一下,小弟才疏学浅如果有大佬在tqimx6ul上成功移植过sdio wifi,也请多多指教,好了现在进入正题吧。     首先我们要搞清楚SDIO WIFI是什么,SDIO WIFI首先是一个...