tarjan求双联通分量(割点,割边)
之前一直对tarjan算法的这几种不同应用比较混淆...我太弱啦!
被BLO暴虐滚过来
用tarjan求点双,很多神犇都给出了比较详细的解释和证明,在这里就不讲了(其实是这只蒟蒻根本不会orz)
这里放一下定义
这篇博客主要讲一讲求割点,点双的板子实现以及详细解释
先yy这样一道题:
有n个点,m条边,保证给出的是一个联通图,求割点
(真·最裸割点)
这道题就可以用下面这份代码实现
- #pragma GCC optimize("O2")
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<stack>
- #include<set>
- #include<map>
- #include<limits.h>
- #include<ctime>
- #define N 100001
- typedef long long ll;
- const int inf=0x3fffffff;
- const int maxn=2017;
- using namespace std;
- inline int read()
- {
- int f=1,x=0;char ch=getchar();
- while(ch>'9'|ch<'0')
- {
- if(ch=='-')
- f=-1;
- ch=getchar();
- }
- while(ch<='9'&&ch>='0')
- {
- x=(x<<3)+(x<<1)+ch-'0';
- ch=getchar();
- }
- return f*x;
- }
- struct tsdl{
- int to,w,next ;
- } edge[N*4];
- int tot,head[N],dfn[N],low[N],fa[N],son[N],size[N];
- bool iscut[N];
- void add(int ui,int vi)
- {
- edge[++tot].next=head[ui];
- edge[tot].to=vi;
- head[ui]=tot;
- }
- void tarjan(int x)
- {
- dfn[x]=low[x]=++tot;
- size[x]=1;
- for(int i=head[x];i!=-1;i=edge[i].next)
- {
- int v=edge[i].to;
- if(v==fa[x])continue;
- if(!dfn[v])
- {
- son[x]++;//x的子树++
- fa[v]=x;//v的父亲是x
- tarjan(v);
- size[x]+=size[v];//x所连节点的个数
- low[x]=min(low[x],low[v]);
- if(dfn[x]<=low[v])
- {
- iscut[x]=1;//找到割点
- }
- }
- else low[x]=min(low[x],dfn[v]);
- }
- if(fa[x]==0&&son[x]<=1)
- iscut[x]=0;//根节点,特判处理
- }
- int main()
- {
- memset(head,-1,sizeof(head));
- int n=read(),m=read();
- for(int i=1;i<=m;i++)
- {
- int u=read(),v=read();
- add(u,v);
- add(v,u);
- }
- for(int i=1;i<=n;i++)
- {
- if(!dfn[i])tarjan(i);
- }
- for(int i=1;i<=n;i++)
- if(iscut[i])cout<<i<<endl;
- }
例如我们输入
5 5
1 2
2 3
1 3
3 4
4 5
程序完美の输出了 3,4
是不是很棒啊x
那么我们要统计点双的数量要怎么处理呢?
显然能发现,我们求出一个割点之后,被割点分成的几部分都能分别与这个割点组成一个点双
那么我们只需要统计每个割点被访问次数即可
更改之后的代码:
- #pragma GCC optimize("O2")
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<algorithm>
- #include<cmath>
- #include<queue>
- #include<stack>
- #include<set>
- #include<map>
- #include<limits.h>
- #include<ctime>
- #define N 100001
- typedef long long ll;
- const int inf=0x3fffffff;
- const int maxn=2017;
- using namespace std;
- inline int read()
- {
- int f=1,x=0;char ch=getchar();
- while(ch>'9'|ch<'0')
- {
- if(ch=='-')
- f=-1;
- ch=getchar();
- }
- while(ch<='9'&&ch>='0')
- {
- x=(x<<3)+(x<<1)+ch-'0';
- ch=getchar();
- }
- return f*x;
- }
- struct tsdl{
- int to,w,next ;
- } edge[N*4];
- int tot,head[N],dfn[N],low[N],fa[N],son[N],size[N];
- bool iscut[N];
- void add(int ui,int vi)
- {
- edge[++tot].next=head[ui];
- edge[tot].to=vi;
- head[ui]=tot;
- }
- int ans;
- void tarjan(int x)
- {
- if(iscut[x])ans++;//统计x1
- dfn[x]=low[x]=++tot;
- size[x]=1;
- int tmp=0;
- for(int i=head[x];i!=-1;i=edge[i].next)
- {
- int v=edge[i].to;
- if(edge[i].to==fa[x])continue;
- if(!dfn[v])
- {
- son[x]++;
- fa[v]=x;
- tarjan(v);
- size[x]+=size[v];
- low[x]=min(low[x],low[v]);
- if(dfn[x]<=low[v])
- {
- iscut[x]=1;//找到割点
- ans++;//统计x2
- }
- }
- else low[x]=min(low[x],dfn[v]);
- }
- if(fa[x]==0&&son[x]<=1)
- iscut[x]=0;//根节点,特判处理
- }
- int main()
- {
- memset(head,-1,sizeof(head));
- int n=read(),m=read();
- for(int i=1;i<=m;i++)
- {
- int u=read(),v=read();
- add(u,v);
- add(v,u);
- }
- for(int i=1;i<=n;i++)
- {
- if(!dfn[i])tarjan(i);
- }
- for(int i=1;i<=n;i++)
- if(iscut[i])cout<<i<<endl;
- cout<<ans;
- }
输出就是直接
当然对他做一点小小的改动也可以实现求桥..
只需要对于每次记录iscut 改为记录二维数组cutedge[x][v]即可
需要注意的是 这里的条件不同于求割点的小于等于 这里需要low[v]严格大于dfn[x]
tarjan求双联通分量(割点,割边)的更多相关文章
- tarjan模板 强联通分量+割点+割边
// https://www.cnblogs.com/stxy-ferryman/p/7779347.html ; struct EDGE { int to, nt; }e[N*N]; int hea ...
- tarjan求强联通分量
tarjan求强联通分量 变量含义说明: pre[i]:i点的被访问的时钟编号,被分配后保持不变 low[i]:i点能访问的最先的点的时钟编号,随子节点改变 scc_no[i]:i点所在的强联通分量的 ...
- [J]computer network tarjan边双联通分量+树的直径
https://odzkskevi.qnssl.com/b660f16d70db1969261cd8b11235ec99?v=1537580031 [2012-2013 ACM Central Reg ...
- POJ 3694Network(Tarjan边双联通分量 + 缩点 + LCA并查集维护)
[题意]: 有N个结点M条边的图,有Q次操作,每次操作在点x, y之间加一条边,加完E(x, y)后还有几个桥(割边),每次操作会累积,影响下一次操作. [思路]: 先用Tarjan求出一开始总的桥的 ...
- BZOJ 压力 tarjan 点双联通分量+树上差分+圆方树
题意 如今,路由器和交换机构建起了互联网的骨架.处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量. 他们每天都生活在巨大的压力之下.小强建立了一个模型.这世界上有N个网络设备, ...
- POJ2942 Knights of the Round Table【Tarjan点双联通分量】【二分图染色】【补图】
LINK 题目大意 有一群人,其中有一些人之间有矛盾,现在要求选出一些人形成一个环,这个环要满足如下条件: 1.人数大于1 2.总人数是奇数 3.有矛盾的人不能相邻 问有多少人不能和任何人形成任何的环 ...
- Tarjan求强联通分量+缩点
提到Tarjan算法就不得不提一提Tarjan这位老人家 Robert Tarjan,计算机科学家,以LCA.强连通分量等算法闻名.他拥有丰富的商业工作经验,1985年开始任教于普林斯顿大学.Tarj ...
- Tarjan算法 (强联通分量 割点 割边)
变量解释: low 指当前节点在同一强连通分量(或环)能回溯到的dfn最小的节点 dfn 指当前节点是第几个被搜到的节点(时间戳) sta 栈 vis 是否在栈中 ans 指强连通分量的数量 top ...
- [hdu2460]network(依次连边并询问图中割边数量) tarjan边双联通分量+lca
题意: 给定一个n个点m条边的无向图,q个操作,每个操作给(x,y)连边并询问此时图中的割边有多少条.(连上的边会一直存在) n<=1e5,m<=2*10^5,q<=1e3,多组数据 ...
随机推荐
- MyBatis - 3.Mapper XML映射文件
SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序): cache – 给定命名空间的缓存配置. cache-ref – 其他命名空间缓存配置的引用. resultMap – 是最复杂也是 ...
- 现在k8s新版里,如何在每个node上运行一个带privileged的daemonset
以前,我们会在kubelet上加--allow-prividged启动参数来实现. 而现在,更推荐的是用pod secureity policy来实现.前面的那种方式以后会被废弃. https://k ...
- spring+redis的集成,redis做缓存
1.前言 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.我们都知道,在日常的应用中,数据库瓶颈是最容易出现的 ...
- BZOJ 1706
题解: 倍增+floyd 首先这题比较容易想到是把每个点拆点做dij 但是这样复杂度是knlogn的 这道题的k较大,所以不行 我们考虑到每走一步,其实就是在进行一次floyd 而这个可以看成矩阵乘法 ...
- java生成二维码并融合模板工具类
二维码融合模板 二维码融合图片 import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.awt.Image; i ...
- 用面向对象重写thread 实现多次调用一个线程
思路: 利用thread类中,run方法在子线程中调用,其他方法在主线程调用,所以将生产者写入主线程,将消费者写入run函数中在子线程中执行,完成生产者消费者模型 注意: 1. 要在 init 函数中 ...
- Java实现简单计算器、抽票程序
计算器: import java.awt.BorderLayout; import java.awt.Container; import java.awt.Font; import java.awt. ...
- Java 之 Web前端(三)
1.JSP a.全称:Java Server Page b.运行:翻译.编译.类装载.类实例化.(初始化.服务.销毁 (这三点为Servlet的生命周期)) 2.JSP的基本组成 a.HTML模板 注 ...
- POJ 2155 Matrix 【二维树状数组】(二维单点查询经典题)
<题目链接> 题目大意: 给出一个初始值全为0的矩阵,对其进行两个操作. 1.给出一个子矩阵的左上角和右上角坐标,这两个坐标所代表的矩阵内0变成1,1变成0. 2.查询某个坐标的点的值. ...
- Spring Boot 项目实战(四)集成 Redis
一.前言 上篇介绍了接口文档工具 Swagger 及项目监控工具 JavaMelody 的集成过程,使项目更加健壮.在 JAVA Web 项目某些场景中,我们需要用缓存解决如热点数据访问的性能问题,业 ...