图论分支-Tarjan初步-边双联通分量
本来应该先说强连通分量,但是有一定的分配,所以这个在下一篇博客将会见到。
这个本想连点连通分量一起讲,但是量有点大,所以我就分两步讲。
我们先看定义

再来看看图解


很容易就能发现,只要将割边断掉,然后剩下的连通块就是我们的边双,那么我们的代码就可以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初步-边双联通分量的更多相关文章
- 图论分支-Tarjan初步-点双连通分量
上一次我们讲到了边双,这次我们来看点双. 说实话来说,点双比边双稍微复杂一些: 学完边双,我们先看一道题 第一问都不用说了吧,多余的道路,明显的割边. 是不是首先想到用边双,但是我们来看一个图: 有点 ...
- 洛谷P2860 [USACO06JAN]冗余路径Redundant Paths(tarjan求边双联通分量)
题目描述 In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1. ...
- POJ 2942 Knights of the Round Table 补图+tarjan求点双联通分量+二分图染色+debug
题面还好,就不描述了 重点说题解: 由于仇恨关系不好处理,所以可以搞补图存不仇恨关系, 如果一个桌子上面的人能坐到一起,显然他们满足能构成一个环 所以跑点双联通分量 求点双联通分量我用的是向栈中pus ...
- 图论分支-Tarjan初步-割点和割边
所谓割点(顶)割边,我们引进一个概念 割点:删掉它之后(删掉所有跟它相连的边),图必然会分裂成两个或两个以上的子图. 割边(桥):删掉一条边后,图必然会分裂成两个或两个以上的子图,又称桥. 这样大家就 ...
- [J]computer network tarjan边双联通分量+树的直径
https://odzkskevi.qnssl.com/b660f16d70db1969261cd8b11235ec99?v=1537580031 [2012-2013 ACM Central Reg ...
- ARC062 - F. Painting Graphs with AtCoDeer (Polya+点双联通分量)
似乎好久都没写博客了....赶快来补一篇 题意 给你一个 \(n\) 个点 , 没有重边和自环的图 . 有 \(m\) 条边 , 每条边可以染 \(1 \to k\) 中的一种颜色 . 对于任意一个简 ...
- 【洛谷 SP2878】Knights of the Round Table(双联通分量)
先放这吧,没时间写,明天再补 "明天到了" 题目链接 题意:求不在任何奇环内的点的数量. Tarjan求点双联通分量,然后再染色判断是不是二分图就好了. 只是不懂为什么Tarjan ...
- 『Tarjan算法 无向图的双联通分量』
无向图的双连通分量 定义:若一张无向连通图不存在割点,则称它为"点双连通图".若一张无向连通图不存在割边,则称它为"边双连通图". 无向图图的极大点双连通子图被 ...
- Tarjan 强连通分量 及 双联通分量(求割点,割边)
Tarjan 强连通分量 及 双联通分量(求割点,割边) 众所周知,Tarjan的三大算法分别为 (1) 有向图的强联通分量 (2) 无向图的双联通分量(求割点,桥) ...
随机推荐
- bzoj1206-[HNOI2005]虚拟内存
卡读的毒瘤题== 看懂之后用map模拟.或者线段树 #include<cstdio> #include<iostream> #include<cmath> #inc ...
- python数学库math模块
函数 数学表示 含义 .pi 圆周率π π的近似值,15位小数 .e 自然常数 e e的近似值,15位小数 ceil(x) [x] 对浮点数向上取整 floor(x) [x] 对浮点数向下取整 pow ...
- BZOJ2342[Shoi2011]双倍回文——回文自动机
题目描述 输入 输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容. 输出 输出文件只有一行,即:输入数据中字符串的最长双倍回文子串的长度,如果双倍回文 ...
- 使用docker部署springboot
首先创建一个简单的springboot web项目 创建一个 DockerController,在其中有一个index()方法,访问时返回:Hello Docker! @RestController ...
- 洛谷P4513 小白逛公园
区间最大子段和模板题.. 维护四个数组:prefix, suffix, sum, tree 假设当前访问节点为cur prefix[cur]=max(prefix[lson],sum[lson]+pr ...
- 一种HBase表数据迁移方法的优化
1.背景调研: 目前存在的hbase数据迁移主要分如下几类: 根据上图,可以看出: 其实主要分为两种方式:(1)hadoop层:因为hbase底层是基于hdfs存储的,所以可以通过把hdfs上的数据拷 ...
- 手机Web 开发中图片img 如何等比例缩放
如果图片本身没有设置 width.height属性的话,只需要修改 max-width:100%; 就可以了 如果图片本身设置了 width.height属性的话,需要同时修改width 和heigh ...
- dll 修复....
之前在安装时总是会碰到缺少什么dll文件,总是头疼的要命,这次很幸运的在网上搜到了这个神奇的小玩意,只需要运行就能够修复缺少的所有的dll文件,所以在这小小的分享一下. 链接:https://pan. ...
- Nginx log日志切割shell
#!/bin/bash#此脚本用于自动分割Nginx的日志,包括access.log和error.log#每天00:00执行此脚本 将前一天的access.log重命名为access-xxxx-xx- ...
- Pro Git
1.安装 Linux: $ yum install git $ apt-get install git windows: 打开 http://git-scm.com/download/win,下载会自 ...