本来应该先说强连通分量,但是有一定的分配,所以这个在下一篇博客将会见到。

这个本想连点连通分量一起讲,但是量有点大,所以我就分两步讲。

我们先看定义

再来看看图解

很容易就能发现,只要将割边断掉,然后剩下的连通块就是我们的边双,那么我们的代码就可以yy出来了,先跑一遍Tarjan求割点,然后在去跑dfs,将每一个边双染色,那么就可以了,而染色操作,以便于我们后面好缩点。

我们来看模板

void Tarjan(int x,int fa){
low[x]=dfn[x]=++t;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(!dfn[y]){
Tarjan(y,i);
low[x]=min(low[y],low[x]);
if(low[y]>dfn[x])
edge[i].flag=edge[(i^)].flag=;//寻找割边 ,并标记
}else if((i^)!=fa){
low[x]=min(low[x],dfn[y]);
}
}
} void dfs(int x){
col[x]=tot;//染色
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(!col[y]&&!edge[i].flag)//不能是走过的点和割边
dfs(y);//遍历
}
} //下面插入主函数中 for(int i=;i<=n;i++){
if(!dfn[i]) Tarjan(i,-);
}
for(int i=;i<=n;i++){
if(!col[i]) ++tot,dfs(i);
}

大家仔细琢磨一下,应该就能懂他的思想了。

然后我们来看例题:

这道题很显然是将这个图变为边双。在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。 这时就要用到我们的染色缩点,缩点请仔细思考怎么做。

缩点后,新图是一棵树,树的边就是原无向图的桥。 现在问题转化为:在树中至少添加多少条边能使图变为双连通图。

此时我们就能想出之前我们学过的入度和出度;

那么,只要将度为一的点连边即可,那么此时我们的公式就是添加边数=(树中度为1的节点数+1)/2;

具体做法就是:

首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。 然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

这样就解决了,来看

Code

#include<bits/stdc++.h>
#define maxn 5007
#define M 20007
using namespace std;
int n,m,t,head[maxn],cent=,low[maxn],dfn[maxn],vis[maxn];
int tot,col[maxn],out[maxn],ans,in[maxn],ol;
struct node{
int next,to,flag,from;
}edge[M<<]; void add(int u,int v){
edge[++cent]=(node){head[u],v,,u};head[u]=cent;
} void Tarjan(int x,int fa){
low[x]=dfn[x]=++t;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(!dfn[y]){
Tarjan(y,i);
low[x]=min(low[y],low[x]);
if(low[y]>dfn[x])
edge[i].flag=edge[(i^)].flag=;//寻找割边 ,并标记
}else if((i^)!=fa){
low[x]=min(low[x],dfn[y]);
}
}
} void dfs(int x){
col[x]=tot;//染色
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(!col[y]&&!edge[i].flag)//不能是走过的点和割边
dfs(y);//遍历
}
} void make_dfs(int x,int fa){
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==fa) continue;
out[y]++,in[x]++;//计算入度出度
make_dfs(y,x);
}
} int main(){
// freopen("rpaths.in","r",stdin);
// freopen("rpaths.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
if(a==b) continue;
add(a,b),add(b,a);
}
for(int i=;i<=n;i++){
if(!dfn[i]) Tarjan(i,-);
}
for(int i=;i<=n;i++){
if(!col[i]) ++tot,dfs(i);
}
memset(head,,sizeof(head));cent=;//预处理
for(int i=;i<=*m+;i++){
if(edge[i].flag){
add(col[edge[i].from],col[edge[i].to]);
}//重点!!!染色缩点建树
}
make_dfs(,-);//寻找度
for(int i=;i<=tot;++i){
if((out[i]==&&in[i]==)||(in[i]==&&!out[i])){
ans++;//统计度为一的点
}
}
printf("%d\n",(ans+)/);
}

下一道也很简单:

我们只要缩点,然后求距离就可以了,LCA求距离都懂的吧?(好像又没讲,下次下次);

我的错,没说LCA,在讲完LCA后再来看这道吧,但看完后就懂代码了:

Code

#include<bits/stdc++.h>
#define maxn 100007
using namespace std;
int n,m,cent=,head[maxn],low[maxn],dfn[maxn],col[maxn],t,fa[maxn][];
int tot,dep[maxn],q;
struct node{
int next,to,from,flag;
}edge[maxn<<]; void add(int u,int v){
edge[++cent]=(node){head[u],v,u,};head[u]=cent;
} void make_dfs(int x,int dy){
dep[x]=dy;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(fa[x][]==y) continue;
fa[y][]=x;
make_dfs(y,dy+);
}
} void Init(){
fa[][]=-;
make_dfs(,);
for(int i=;i<=;i++){
for(int j=;j<=tot;j++){
if(fa[j][i-]<) fa[j][i]=-;
else fa[j][i]=fa[fa[j][i-]][i-];
}
}
return ;
} int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=,d=dep[x]-dep[y];d;d>>=,i++){
if(d&) x=fa[x][i];
}
if(x==y) return x;
for(int i=;i>=;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][];
} void Tarjan(int x,int f){
low[x]=dfn[x]=++t;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(!dfn[y]){
Tarjan(y,i);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x])
edge[i].flag=edge[(i^)].flag=;
}else if((i^)!=f){
low[x]=min(low[x],dfn[y]);
}
}
} void dfs(int x){
col[x]=tot;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(!col[y]&&!edge[i].flag)
dfs(y);
}
} int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
for(int i=;i<=n;i++){
if(!dfn[i]) Tarjan(i,-);
}
for(int i=;i<=n;i++){
if(!col[i]) ++tot,dfs(i);
}
memset(head,,sizeof(head));cent=;
for(int i=;i<=*m+;i++){
if(edge[i].flag){
add(col[edge[i].from],col[edge[i].to]);
}//缩点套路,一定要会
}
Init();//LCA初始化
scanf("%d",&q);
for(int i=,a,b;i<=q;i++){
scanf("%d%d",&a,&b);
printf("%d\n",dep[col[a]]+dep[col[b]]-*dep[lca(col[a],col[b])]);//求距离
}
}

这就差不多结束了,自己可以在找些题,要

深刻理解缩点的重要性

图论分支-Tarjan初步-边双联通分量的更多相关文章

  1. 图论分支-Tarjan初步-点双连通分量

    上一次我们讲到了边双,这次我们来看点双. 说实话来说,点双比边双稍微复杂一些: 学完边双,我们先看一道题 第一问都不用说了吧,多余的道路,明显的割边. 是不是首先想到用边双,但是我们来看一个图: 有点 ...

  2. 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths(tarjan求边双联通分量)

    题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...

  3. POJ 2942 Knights of the Round Table 补图+tarjan求点双联通分量+二分图染色+debug

    题面还好,就不描述了 重点说题解: 由于仇恨关系不好处理,所以可以搞补图存不仇恨关系, 如果一个桌子上面的人能坐到一起,显然他们满足能构成一个环 所以跑点双联通分量 求点双联通分量我用的是向栈中pus ...

  4. 图论分支-Tarjan初步-割点和割边

    所谓割点(顶)割边,我们引进一个概念 割点:删掉它之后(删掉所有跟它相连的边),图必然会分裂成两个或两个以上的子图. 割边(桥):删掉一条边后,图必然会分裂成两个或两个以上的子图,又称桥. 这样大家就 ...

  5. [J]computer network tarjan边双联通分量+树的直径

    https://odzkskevi.qnssl.com/b660f16d70db1969261cd8b11235ec99?v=1537580031 [2012-2013 ACM Central Reg ...

  6. ARC062 - F. Painting Graphs with AtCoDeer (Polya+点双联通分量)

    似乎好久都没写博客了....赶快来补一篇 题意 给你一个 \(n\) 个点 , 没有重边和自环的图 . 有 \(m\) 条边 , 每条边可以染 \(1 \to k\) 中的一种颜色 . 对于任意一个简 ...

  7. 【洛谷 SP2878】Knights of the Round Table(双联通分量)

    先放这吧,没时间写,明天再补 "明天到了" 题目链接 题意:求不在任何奇环内的点的数量. Tarjan求点双联通分量,然后再染色判断是不是二分图就好了. 只是不懂为什么Tarjan ...

  8. 『Tarjan算法 无向图的双联通分量』

    无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...

  9. Tarjan 强连通分量 及 双联通分量(求割点,割边)

    Tarjan 强连通分量 及 双联通分量(求割点,割边) 众所周知,Tarjan的三大算法分别为 (1)         有向图的强联通分量 (2)         无向图的双联通分量(求割点,桥) ...

随机推荐

  1. RESTful 架构详解

    RESTful 架构详解 分类 编程技术 1. 什么是REST REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移. 它首次 ...

  2. BZOJ2877 NOI2012魔幻棋盘(二维线段树)

    显然一个序列的gcd=gcd(其差分序列的gcd,序列中第一个数).于是一维情况直接线段树维护差分序列即可. 容易想到将该做法拓展到二维.于是考虑维护二维差分,查询时对差分矩阵求矩形的gcd,再对矩形 ...

  3. 五一培训 清北学堂 DAY2

    今天还是冯哲老师的讲授~~ 今日内容:简单数据结构(没看出来简单qaq) 1.搜索二叉树 前置技能 一道入门题在初学OI的时候,总会遇到这么一道题.给出N次操作,每次加入一个数,或者询问当前所有数的最 ...

  4. 安装 xadmin 报错: Command "python setup.py egg_info" failed with error code 1 in C:\Users\Python\AppData\Local\Temp\pip-install-1k1byg0p\xadmin\

    报错详情 安装 xadmin 组件的时候报错 不论是命令行还是 pycharm 方式都不行 分析报错 按照报错提示是说 README.rst 文件的编码问题导致. 解决报错 通过 github 下载源 ...

  5. C#常用的命名规则汇总

    C#常用的命名规则汇总 来源 https://www.cnblogs.com/pengyouqiang88/p/5021128.html 本文转载自脚本之家 本文详细汇总了C#常用的命名规则.分享给大 ...

  6. google 搜索关键字技巧

    google 搜索关键字技巧 来源  https://www.cnblogs.com/qiudabai/articles/9143328.html inurl: 用于搜索网页上包含的URL. 这个语法 ...

  7. Leetcode 27.移除元素 By Python

    给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...

  8. [TJOI2012]桥(最短路+线段树)

    有n个岛屿, m座桥,每座桥连通两座岛屿,桥上会有一些敌人,玩家只有消灭了桥上的敌人才能通过,与此同时桥上的敌人会对玩家造成一定伤害.而且会有一个大Boss镇守一座桥,以玩家目前的能力,是不可能通过的 ...

  9. docker命令篇

    基础命令: 镜像: 获取镜像 $ docker pull centos:7 下拉自己仓库镜像,在后面仓库部分会讲到. 列出镜像: $ docker image ls 删除镜像: $ docker im ...

  10. Qt5应用改变窗口大小时出现黑影

    解决方法 在启动程序时,添加-platform wayland参数 添加QT_QPA_PLATFORM=wayland-egl到系统环境变量 注意:改完后虽然没有黑影,但软件图标显示不正常,也不能正常 ...