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

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

我们先看定义

再来看看图解

很容易就能发现,只要将割边断掉,然后剩下的连通块就是我们的边双,那么我们的代码就可以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. #189 stat(动态规划)

    容易想到固定第一个排列为1~n,算出答案后乘上n!即可,但这样就离正解走远了. 考虑排列dp的一般套路,将数从大到小加入排列,这样每个位置第一次填数时(不管是第一个还是第二个排列)其贡献就确定了. 显 ...

  2. 洛谷P1119灾后重建

    题目 做一个替我们首先要明确一下数据范围,n<=200,说明n^3的算法是可以过得,而且这个题很明显是一个图论题, 所以我们很容易想到这个题可以用folyd, 但是我在做这个题的时候因为没有深刻 ...

  3. 洛谷P3381 最小费用最大流

    费用流板子 还是一道板子题..先练练手 #include <bits/stdc++.h> #define INF 0x3f3f3f3f #define full(a, b) memset( ...

  4. bzoj 1067: [SCOI2007]降雨量 (离散化+线段树)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1067 思路: 毒瘤题,写的自闭,改了一晚上,注意要理清题目的逻辑 x小于等于y,x,y之间的 ...

  5. 网络流相关知识点以及题目//POJ1273 POJ 3436 POJ2112 POJ 1149

    首先来认识一下网络流中最大流的问题 给定一个有向图G=(V,E),把图中的边看做成管道,边权看做成每根管道能通过的最大流量(容量),给定源点s和汇点t,在源点有一个水源,在汇点有一个蓄水池,问s-t的 ...

  6. word 2013 题注、图注、插入图片自动修改大小、批量更新题注编号

    1 .题注 图片下面的文字说明,如  图 1.1.1 2.图注 图的标题格式,可以右键修改段落为居中,选中图片,点下此格式快捷居中等其他格式 3. 题注插入 效果 如下 4.题注自动居中对齐 先点击图 ...

  7. 使用 windows server 2012 性能监视器

    监控Windows server的内存.CPU.磁盘IO等性能 配置方法: 打开Aministrator Tools --> Performance Monitor Performances - ...

  8. 【题解】 bzoj1135: [POI2009]Lyz (线段树+霍尔定理)

    题面戳我 Solution 二分图是显然的,用二分图匹配显然在这个范围会炸的很惨,我们考虑用霍尔定理. 我们任意选取穿\(l,r\)的号码鞋子的人,那么这些人可以穿的鞋子的范围是\(l,r+d\),这 ...

  9. HDU46093-idiots

    题目大意 给一堆边的长度,问从中随机选出三条边来能够组成三角形的概率. 题解 其实就是要求能够组成三角形的方案数.直接从三条边入手问题会很复杂,所以我们可以先求出f[x]表示随便选出两条边长度之和为x ...

  10. [APIO2013]机器人(斯坦纳树)

    题目描述 VRI(Voltron 机器人学会)的工程师建造了 n 个机器人.任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人. 我们把机器人用 1 至 n 编号(n ≤ 9).如果两个机 ...