图论分支-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) 无向图的双联通分量(求割点,桥) ...
随机推荐
- linq之group by 的使用
group by var list = from s in _sysBll.GetList(s => s.ParamID == "TraSchType" && ...
- GitHub大佬:供计算机学习鉴黄功能的图片数据库
ps:学无止境 想要构建一套鉴黄系统,必须有大量的真实图片供计算机进行学习,以便于区分开正常图片和黄色图片. 近期有位加拿大程序员在Github上传了图片列表,里面包含了大量图片地址可以供计算机进行学 ...
- HTML查漏补缺 【未完】
1.命名锚 HTML 链接 - name 属性 name 属性规定锚(anchor)的名称. 您可以使用 name 属性创建 HTML 页面中的书签. 书签不会以任何特殊方式显示,它对读者是不可见的. ...
- 性能测试工具 Locust
https://docs.locust.io/en/latest/quickstart.html
- 大学实验3指导:利用单链表实现A-B
实验目的:深入理解单链表的建立及操作 实验内容: 1.建立单链表A与B 2.实现主要的函数,查找.插入.删除等 3.实现操作A-B 步骤1:包含必要的函数库,对结构体LNode中的抽象数据类型Elem ...
- hbase系列
jvmhttps://www.cnblogs.com/jiyukai/p/6665199.html hbase https://blog.csdn.net/lizhitao/article/detai ...
- 【XSY2730】Ball 多项式exp 多项式ln 多项式开根 常系数线性递推 DP
题目大意 一行有\(n\)个球,现在将这些球分成\(k\) 组,每组可以有一个球或相邻两个球.一个球只能在至多一个组中(可以不在任何组中).求对于\(1\leq k\leq m\)的所有\(k\)分别 ...
- Hdoj 2018.母牛的故事 题解
Problem Description 有一头母牛,它每年年初生一头小母牛.每头小母牛从第四个年头开始,每年年初也生一头小母牛.请编程实现在第n年的时候,共有多少头母牛? Input 输入数据由多个测 ...
- 【JDK源码】将JDK源码导入IDEA中
新建工程 在IDEA中新建普通JAVA工程,步骤如下: 导入源码 首先可以通过如下方法找到工程目录. 在JDK安装目录下找到源码包src.zip,如下图 将src.zip包解压,并将src目录下的内容 ...
- 「九省联考 2018」IIIDX 解题报告
「九省联考 2018」IIIDX 这什么鬼题,送的55分要拿稳,实测有60? 考虑把数值从大到小摆好,每个位置\(i\)维护一个\(f_i\),表示\(i\)左边比它大的(包括自己)还有几个数可以选 ...