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

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

我们先看定义

再来看看图解

很容易就能发现,只要将割边断掉,然后剩下的连通块就是我们的边双,那么我们的代码就可以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. 在没有 Emacs 的情况下使用 Org 模式

    导读 每到年初似乎总有这么一个疯狂的冲动来寻找提高生产率的方法.新年决心,正确地开始一年的冲动,以及“向前看”的态度都是这种冲动的表现.软件推荐通常都会选择闭源和专利软件.但这不是必须的. 这是我 2 ...

  2. H.264编码原理以及I帧B帧P帧

    前言 ----------------------- H264是新一代的编码标准,以高压缩高质量和支持多种网络的流媒体传输著称,在编码方面,我理解的他的理论依据是:参照一段时间内图像的统计结果表明,在 ...

  3. 使用java代码批量删除新浪微博

    首先开骂,新浪微博@#@!,不经我同意就转发各种微博,制造垃圾,还不提供微博批量删除功能,摆明了的流氓行为,可耻可恨,遭人唾弃! SSLClient.java import org.apache.ht ...

  4. Educational Codeforces Round 53 (Rated for Div. 2) C. Vasya and Robot

    题意:给出一段操作序列 和目的地 问修改(只可以更改 不可以删除或添加)该序列使得最后到达终点时  所进行的修改代价最小是多少 其中代价的定义是  终点序号-起点序号-1 思路:因为代价是终点序号减去 ...

  5. POI中不推荐的方法与其替代的方法

    不推荐getCellType(),推荐getCellTypeEnum() if(tcell.getCellTypeEnum() == CellType.NUMERIC){ System.out.pri ...

  6. scrapy简单使用

    #settings.py文件设置 #如果网站中没有robots文件,就不会抓取任何数据 ROBOTSTXT_OBEY = False #设置请求头 DEFAULT_REQUEST_HEADERS = ...

  7. ssh-keygen适用场景与rsync使用id_rsa技巧

    ssh-keygen工具可以实现免密码登录服务器可参考之前的blog:http://www.cnblogs.com/Mrhuangrui/p/4565333.html写的比较粗糙 原理说明使用ssh- ...

  8. UVA10559 Blocks(区间dp)

    有n个带有颜色的方块,没消除一段长度为x的连续的相同颜色的方块可以得到x^2的分数,让你用一种最优的顺序消除所有方块使得得分最多. 输入格式 第一行包含测试的次数t(1≤t≤15) 每个案例包含两行. ...

  9. Testlink解决大用例导入问题

    最近公司同事需要将别的testlink的用例迁移过来,由于现在新的服务器也在使用,不能使用数据库导入的办法,只能用xml文件进行导入,不过在导入的时候出现了个没遇到的问题,报错文件太大,无法上传. 解 ...

  10. pip无法正常使用卸载并重新安装

    错误提示 ➜ ~ pip Traceback (most recent call last): File "/usr/bin/pip", line 11, in <modul ...