by szTom

前置知识

  1. 邻接表存储及遍历图
  2. tarjan求强连通分量

割点

割点的定义

在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合。

也就是说,就是有个点维持着连通分量的继续,去掉那个点,这个连通分量就无法在维持下去,分成好几个连通分量。

比如说,下图中



蓝色的点就是割点。

tarjan求割点

前向边

首先,先了解什么是前向边:

将这个无向图按树排列,从子节点到其祖先的边为前向边。

即为 \(low[x] < dfn[x]\) 时,有到x的前向边。

因为,low的定义是:

\[low[x]=
min\{dfn[y]\ |\ (x,y) \in E\
y\not = father[x] \}
$$即从$x$所能到达的点中$dfn[x]$最小的点。
### 方案

可以得出,设$y$是$x$的任意儿子,满足$$dfn[x] \leq low[y]$$的点$x$就是割点。
同时,当$x$为根节点且$x$的儿子数量多于$1$时,$x$是割点。

### 原理
如果一个点不是根节点,那么当它$dfn[x] \leq low[y]$时,没有关于$x$的前向边。这时删除点$x$的话,$x$的儿子节点就无法到达$x$的祖先了,故$x$是割点。
如果$x$是根节点,那么当它的儿子数量多于$1$时,删除$x$,其儿子节点无法互相到达,故$x$是割点。

## 代码实现
下面是[P3388 【模板】割点(割顶)](https://www.luogu.org/problem/P3388)的代码。
其中用`bool iscut[20005];`来记录割点。
```cpp
vector<int> G[20005];
int dfn[20005], low[20005];
int n, m;
int sum, root;
bool iscut[20005];

void tarjan(int x) {
int flag = 0;
dfn[x] = low[x] = ++sum;
for (unsigned i = 0; i < G[x].size(); ++i) {
int y = G[x][i];
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[y], low[x]);
if (low[y] >= dfn[x]) {
++flag;
if (x != root || flag > 1) iscut[x] = 1;
}
} else {
low[x] = min(low[x], dfn[y]);
}
}
}

int main() {
int x, y;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
cin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}

for ( int i = 1; i <= n; ++i) {
if (!dfn[i]) {
root = i;
tarjan(i);
}
}

int ans = 0;
for (int i = 1; i <= n; ++i) {
if (iscut[i]) ++ans;
}

cout << ans << endl;

for (int i = 1; i <= n; ++i) {
if (iscut[i]) cout << i << " ";
}
cout << endl;
return 0;
}
```

# 桥
## 定义
> 假设有连通图$G=\{V,E\}$,$e$是其中一条边(即$e \in E$),如果$G-e$是不连通的,则边$e$是图$G$的一条割边(桥)。

比如说,下图中,
![M5Wqqs.png-割边示例图](https://s2.ax1x.com/2019/11/21/M5Wqqs.png)
红色箭头指向的就是割边。

## tarjan求割边
和割点差不多,当存在边$(x,y) \in E$时,满足:$$dfn[x] < low[y]$$时,边$(x,y)$是一条割边。

## 代码实现
下面代码实现了求割边,其中,当`isbridge[x]`为真时,$(father[x],x)$为一条割边。
```cpp
int low[MAXN], dfn[MAXN], iscut[MAXN], dfs_clock;
bool isbridge[MAXN];
vector<int> G[MAXN];
int cnt_bridge;
int father[MAXN];

void tarjan(int u, int fa) {
father[u] = fa;
low[u] = dfn[u] = ++dfs_clock;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u]) {
isbridge[v]=true;
++cnt_bridge;
}
} else if(dfn[v] < dfn[u] && v != fa) {
low[u] = min(low[u], dfn[v]);
}
}
}
```

# 关于前向星(链式邻接表)
## 前向星的定义
> 一种数据结构,以储存边的方式来存储图。其优势有方便标记某一条边。

## 为什么要提前向星
本文中已经避免使用前向星,但网上绝大部分博客都使用提前向星。
下面不是前向星的教程(请自行百度),但可以帮助理解前向星。

## 理解前向星

1.遍历边
```cpp
for (int i = head[x]; i; i = nxt[u]) {
int y = to[i], w = weight[i];
//code
}
```
相当于:
```cpp
for (unsigned i = 0; i < G[x].size(); ++i) {
int y = G[x][i].x, w = G[x][i].w;
// code
}
```

2.相反边
`i`和`i ^ 1`互为反向边的编号。

邻接表要处理这种查询,可以新加一个变量记录:
```cpp
struct node {
int x, w; //original codes
int partner;
};
```

在建边时,加上:
```cpp
int x, y, w;
for (int i = 1; i <= m; ++i) {
cin >> x >> y >> w;
G[x].push_back((node) {y, w, G[y].size()});
G[y].push_back((node) {x, w, G[x].size() - 1});
}
```

需要时,`G[x][i]`的反向边是`G[G[x][i].x][G[x][i].partner]`

## 效率问题
容易看出,前向星的空间效率的常数比邻接表大。时间复杂度上理论相等,但因为`vector`的常数大,时间常数比较大。

# tarjan的复杂度
和模板没有区别,时间是$O(n+m)$,空间$O(n+m)$(要存图)

# 关于割点与桥的其他问题

## 定理?猜想?
可能会发现,割点与桥有如下性质:
> (1) 两个割点之间的边是割边。
> (2) 割边连接的点是有割点。

(1)这在很多情况上是正确无误的,但有反例,如:

![MTulBq.png](https://s2.ax1x.com/2019/11/22/MTulBq.png)

(2)除了两点一边的情况,是正确的。
证明嘛:~~略~~

# 练习题
## [P3388 【模板】割点(割顶)](https://www.luogu.org/problem/P3388)
模板题,割点。

## [POJ2117 Electricity](https://vjudge.net/problem/POJ-2117)
割点。

## [HDU4738 Caocao's Bridges](https://vjudge.net/problem/HDU-4738)
割边,不需要记录割边,tarjan时直接记录答案。
像这样:
```cpp
low[u] = min(low[u],low[v]);
if (low[v] > dfn[u]) {
ans = min(ans, G[v][i].w);
}
```

## [HDU2460 Network](https://vjudge.net/problem/HDU-2460)
割边 + LCA(暴力可以)

## [POJ1523 SPF](https://vjudge.net/problem/POJ-1523)
割点 + dfs。

@2019-11-21 广州市第二中学科技城校区\]

Tarjan求割点和桥的更多相关文章

  1. UESTC 900 方老师炸弹 --Tarjan求割点及删点后连通分量数

    Tarjan算法. 1.若u为根,且度大于1,则为割点 2.若u不为根,如果low[v]>=dfn[u],则u为割点(出现重边时可能导致等号,要判重边) 3.若low[v]>dfn[u], ...

  2. poj_1144Network(tarjan求割点)

    poj_1144Network(tarjan求割点) 标签: tarjan 割点割边模板 题目链接 Network Time Limit: 1000MS Memory Limit: 10000K To ...

  3. tarjan求割点割边的思考

    这个文章的思路是按照这里来的.这里讨论的都是无向图.应该有向图也差不多. 1.如何求割点 首先来看求割点.割点必须满足去掉其以后,图被分割.tarjan算法考虑了两个: 根节点如果有两颗及以上子树,它 ...

  4. tarjan求割点与割边

    tarjan求割点与割边 洛谷P3388 [模板]割点(割顶) 割点 解题思路: 求割点和割点数量模版,对于(u,v)如果low[v]>=dfn[u]那么u为割点,特判根结点,若根结点子树有超过 ...

  5. POJ 1144 Network(Tarjan求割点)

    Network Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 12707   Accepted: 5835 Descript ...

  6. poj 1523 SPF(tarjan求割点)

    本文出自   http://blog.csdn.net/shuangde800 ------------------------------------------------------------ ...

  7. 洛谷P3388 【模板】割点(割顶)(tarjan求割点)

    题目背景 割点 题目描述 给出一个n个点,m条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n,m 下面m行每行输入x,y表示x到y有一条边 输出格式: 第一行输出割点个数 第二行按照 ...

  8. [POJ1144][BZOJ2730]tarjan求割点

    求割点 一种显然的n^2做法: 枚举每个点,去掉该点连出的边,然后判断整个图是否联通 用tarjan求割点: 分情况讨论 如果是root的话,其为割点当且仅当下方有两棵及以上的子树 其他情况 设当前节 ...

  9. poj1144 tarjan求割点

    poj1144 tarjan求割点 额,算法没什么好说的,只是这道题的读入非常恶心. 注意,当前点x是否是割点,与low[x]无关,只和low[son]和dfn[x]有关. 还有,默代码的时候记住分目 ...

随机推荐

  1. 如何用for..of.. 遍历一个普通的对象?

    如何用for..of.. 遍历一个普通的对象? 首先了解一下for..of..: 它是es6新增的一个遍历方法,但只限于迭代器(iterator), 所以普通的对象用for..of遍历 是会报错的.下 ...

  2. java 内省综合案例和Beanutils工具包

    演示用eclipse自动生成 ReflectPoint类的setter和getter方法. 直接new一个PropertyDescriptor对象的方式来让大家了解JavaBean API的价值,先用 ...

  3. linux 在 scull 中使用旗标

    旗标机制给予 scull 一个工具, 可以在存取 scull_dev 数据结构时用来避免竞争情况. 但是正确使用这个工具是我们的责任. 正确使用加锁原语的关键是严密地指定要保护哪个 资源并且确认每个对 ...

  4. JDK自带的native2ascii工具介绍

    背景:在做Java开发的时候,常常会出现一些乱码,或者无法正确识别或读取的文件,比如常见的validator验证用的消息资源(properties)文件就需要进行Unicode重新编码.原因是java ...

  5. Linux 内核设备注册

    通常的注册和注销函数在: int device_register(struct device *dev); void device_unregister(struct device *dev); 我们 ...

  6. <数论相关>欧几里得与拓展欧几里得证明及应用

    欧几里得算法 欧几里得算法的复杂度为O(log(n)),是一个非常高效的求最大公约数算法. 在这里不证明欧几里得算法的复杂度,有兴趣的可以访问以下链接:http://blog.sina.com.cn/ ...

  7. jekyll 在博客添加流程图

    本文告诉大家如何在博客使用流程图. 如果你使用的是我博客的模板,那么就可以直接使用我说的文件,如果是自己的主题,就需要在自己文件对应的地方加上代码. 在我的博客里,需要添加下面的js到博客,可以打开 ...

  8. Keras lstm 文本分类示例

    #基于IMDB数据集的简单文本分类任务 #一层embedding层+一层lstm层+一层全连接层 #基于Keras 2.1.1 Tensorflow 1.4.0 代码: '''Trains an LS ...

  9. (转)hibernate缓存机制详细分析

    在本篇随笔里将会分析一下hibernate的缓存机制,包括一级缓存(session级别).二级缓存(sessionFactory级别)以及查询缓存,当然还要讨论下我们的N+1的问题. 随笔虽长,但我相 ...

  10. PHP常用函数拾遗

     PHP中常用过滤函数addslashes().mysql_real_escape_string().mysql_escape_string() 如addslashes().mysql_real_es ...