Tarjan 连通性
Tarjan 连通性
Tarjan 爷爷的代表作,图的连通性问题直接解决
两个核心数组:
- \(dfn_u\):\(u\) 的 dfs 序
- \(low_u\):\(u\) 及 \(u\) 的后代通过返祖边能回到的最小的 \(dfn\)
四种边
- 树边:dfs 搜索树中的边
- 返祖边:若在搜索树中, \(i\) 是 \(j\) 的祖先,则原图中从 \(j\) 到 \(i\) 的边是返祖边
- 前向边:若在搜索树中, \(i\) 是 \(j\) 的后代,则原图中从 \(j\) 到 \(i\) 的边是前向边
- 交叉边:若在搜索树中, \(i\) 和 \(j\) 不在同一子树,则从 \(i\) 到 \(j\) 的边是交叉边
前向边、交叉边只存在于有向图
核心代码
int dfn[N],low[N],clk;
void tarjan(int u,int fa) {
dfn[u]=low[u]=++clk;
for(int i=lst[u],v;i;i=nxt[i]) {
if(!dfn[v=to[i]]) {
tarjan(v,u);
low[u]=min(low[u],low[v]);
} else if(这是返祖边)
low[u]=min(low[u],dfn[v]);
}
}
int main() {
// Input ....
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i,-1);
}
无向图
无向图的操作与连通分量有关
- 连通分量:无向图的极大连通子图
割点
定义:删去一个点使得图中联通分量增加,该点叫做割点
求法:如果 \(u\) 存在一个儿子 \(v\) 且 \(low_v\ge dfn_u\)
说明删去 \(u\) 后 \(v\) 不能通过返祖边连回 \(u\) 的祖先, \(u\) 是割点
特判:如果 \(u\) 是搜索树的根且只有 1 个儿子,则 \(u\) 不是割点
割边(桥)
定义:删去一条边使得图中联通分量增加,该点叫做割边(桥)
求法:如果对于一条边 \((u,v)\) 且 \(low_v>dfn_u\)
说明删去该边后 \(v\) 不能通过返祖边连回 \(u\) 及 \(u\) 的祖先,该边是割边(桥)
割点 & 桥 的实现
int cut[N],br[M<<1];
void tarjan(int u,int fa) {
dfn[u]=low[u]=++clk;
register int ch=0;
for(int i=lst[u],v;i;i=nxt[i]) {
if(!dfn[v=to[i]]) {
++ch;
tarjan(v,u);
if(low[v]>=dfn[u])cut[u]=1;
if(low[v]>dfn[u])br[i]=br[i^1]=1;
low[u]=min(low[u],low[v]);
} else if(v^fa)
low[u]=min(low[u],dfn[v]);
}
if(fa==-1 && ch==1)cut[u]=0;
}
点双连通分量
定义:无向图的极大无割点子图 ,简称点双
求法:一个点(如割点)可能在多个点双中,而一条边只能在一个点双中
考虑用栈存下边,当 \(u\) 是割点时,一直弹栈直到弹出边 \((u,v)\),弹出的边组成点双
vector<int>bs[N];
int id[N],tot;
void tarjan(int u,int fa) {
dfn[u]=low[u]=++clk;
for(int i=lst[u],v,nw;i;i=nxt[i]) {
if(!dfn[v=to[i]]) {
s[++top]=i;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]) {
tot++;
while(1) {
nw=s[top--];
if(id[to[nw]]!=tot)
id[to[nw]]=tot,bs[tot].pb(to[nw]);
if(id[to[nw^1]]!=tot)
id[to[nw^1]]=tot,bs[tot].pb(to[nw^1]);
if(to[nw]==v && to[nw^1]==u)break;
}
}
} else if(v!=fa && dfn[v]<dfn[u]) {
low[u]=min(low[u],dfn[v]);
s[++top]=i;
}
}
}
边双连通分量
- 定义:无向图的极大无桥子图,简称边双
- 求法:可用点双的思想,把点压栈,然后遇到桥弹点到弹出 \(u\) ,弹出的点组成边双
- 求法 2:更简单。第一次 \(dfs\) 求出桥,第二次 \(dfs\) 打标记,遇到桥就编号加 1 ,编号相同的点构成边双
void tarjan(int u,int fa) {
dfn[u]=low[u]=++clk;
for(int i=lst[u],v;i;i=nxt[i]) {
if(!dfn[v=to[i]]) {
++ch;
tarjan(v,u);
if(low[v]>dfn[u])br[i]=br[i^1]=1;
low[u]=min(low[u],low[v]);
} else if(v^fa)
low[u]=min(low[u],dfn[v]);
}
}
int tot;
void dfs(int u,int fa,int nw) {
cl[u]=nw;
for(int i=lst[u];i;i=nxt[i])
if((v=to[i])^fa) {
if(br[i])++tot,dfs(v,u,tot);
else dfs(v,u,nw);
}
}
有向图
强连通分量
有向图中 tarjan 用于求强连通分量
- 强连通图:一个任意两点都可以相互到达的有向图
- 强连通分量:一个有向图的极大的强连通子图
求法:将点入栈,遇到 \(dfn_u=low_u\) 时就弹栈顶直到弹出 \(u\) ,弹出的点在一个强连通分量
当 \(low_u<dfn_u\) 时说明强连通分量还可以扩大,所以只能 \(dfn_u=low_u\)
int sz[N],cl[N],tot;
void tarjan(int u) {
dfn[u]=low[u]=++clk,s[++top]=u;
for(int i=lst1[u],v;i;i=nxt1[i]) {
if(!dfn[v=to1[i]]) {
tarjan(v);
low[u]=min(low[u],low[v]);
} else if(!cl[v]) {
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u]) {
++tot,sz[tot]=1;
while(s[top]!=u)
++sz[tot],cl[s[top]]=tot,--top;
cl[u]=tot,--top;
}
}
缩点
作用:将强连通分量看成一个点,可以将原图缩成一个 DAG
可以用于方便地做 dp
做法:求强连通分量给点染色,然后枚举一个每条边 \((u,v)\)
如果 \(color_u\ne color_v\) 就从 \(color_u\) 向 \(color_v\) 连边
for(int i=1;i<=n;i++)
for(int j=lst[i];j;j=nxt[j])
if(cl[i]!=cl[to[j]])
Ae2(cl[i],cl[to[j]]);
例
缩点后一定是树,在 \(\text{dfs}\) 序上 dp
设 \(f_{i, j}\) 为做到了 \(dfs\) 序为 \(i\) 的点用了空间为 \(j\) 的最大价值
设 \(\text{dfs}\) 序为 \(i\) 的点是 \(u\) 。\(w\) 是所用空间,\(v\) 是价值
刷表,对于 \(i\) 选或不选讨论
\(f_{i+1,j+w_u} = \max(f_{i,j}+v_i)\)
\(f_{i+sz_u,j}=\max(f_{i,j})\)
处理依赖:从根到该点路径上(不包括该店)的 \(w\) 之和为必须要的空间
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 105;
int n, m, a[N], Ecnt1, lst1[N], b[N], clk;
int dfn[N], st[N], top, cl[N], low[N], tot;
int w[N], v[N], lst2[N], Ecnt2;
int rk[N], sz[N], pre[N], rd[N], f[N][505];
struct Ed { int to, nxt; } e1[N], e2[N];
inline void cmx(int &x, int y) { x < y ? x = y : 1; }
inline void Ae1(int fr, int go) { e1[++Ecnt1] = (Ed){ go, lst1[fr] }, lst1[fr] = Ecnt1; }
inline void Ae2(int fr, int go) { e2[++Ecnt2] = (Ed){ go, lst2[fr] }, lst2[fr] = Ecnt2; }
void tarjan(int u) {
dfn[u] = low[u] = ++clk, st[++top] = u;
for (int i = lst1[u], v; i; i = e1[i].nxt) {
if (!dfn[v = e1[i].to]) {
tarjan(v), low[u] = min(low[u], low[v]);
} else if (!cl[v]) low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
register int o;
++tot;
while (st[top] ^ u)
o = st[top--], cl[o] = tot, w[tot] += a[o], v[tot] += b[o];
cl[u] = tot, w[tot] += a[u], v[tot] += b[u], --top;
}
}
void dfs(int u) {
rk[++clk] = u, sz[u] = 1;
for (int i = lst2[u], v; i; i = e2[i].nxt)
pre[v = e2[i].to] = pre[u] + w[u], dfs(v), sz[u] += sz[v];
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1, u; i <= n; i++) {
scanf("%d", &u);
if (u) Ae1(u, i);
}
for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; i++)
for (int j = lst1[i], v; j; j = e1[j].nxt)
if (cl[i] ^ cl[v = e1[j].to]) ++rd[cl[v]], Ae2(cl[i], cl[v]);
for (int i = 1; i <= tot; i++) if (!rd[i]) Ae2(0, i);
clk = 0, dfs(0);
for (int i = 1, u; i <= clk; i++) {
u = rk[i];
for (int j = pre[u]; j <= m; j++) {
if (j + w[u] <= m) cmx(f[i + 1][j + w[u]], f[i][j] + v[u]);
cmx(f[i + sz[u]][j], f[i][j]);
}
}
printf("%d", f[clk + 1][m]);
}
Tarjan 连通性的更多相关文章
- BZOJ5279: [Usaco2018 Open]Disruption
题目大意:给你一棵n个节点的树,这n条边称为原边,另给出m条带权值的额外边,求删去每条原边后通过给出的m额外条边变回一棵树的最小价值.题解:看完题面以为是Tarjan连通性之类的题目,冷静分析后想到是 ...
- hihoCoder 1185 连通性·三(Tarjan缩点+暴力DFS)
#1185 : 连通性·三 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 暑假到了!!小Hi和小Ho为了体验生活,来到了住在大草原的约翰家.今天一大早,约翰因为有事要出 ...
- hihoCoder 1183 连通性一·割边与割点(Tarjan求割点与割边)
#1183 : 连通性一·割边与割点 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢 ...
- 图连通性【tarjan点双连通分量、边双联通分量】【无向图】
根据 李煜东大牛:图连通性若干拓展问题探讨 ppt学习. 有割点不一定有割边,有割边不一定有割点. 理解low[u]的定义很重要. 1.无向图求割点.点双联通分量: 如果对一条边(x,y),如果low ...
- 关于连通性问题的Tarjan算法暂结
关于基础知识的预备桥和割点.双联通分量.强连通分量,支配树.(并不会支配树) 关于有向图的Tarjan,是在熟悉不过的了,它的主要功能就是求强联通分量,缩个点,但是要注意一下构建新图的时候有可能出现重 ...
- tarjan算法与无向图的连通性(割点,桥,双连通分量,缩点)
基本概念 给定无向连通图G = (V, E)割点:对于x∈V,从图中删去节点x以及所有与x关联的边之后,G分裂为两个或两个以上不相连的子图,则称x为割点割边(桥)若对于e∈E,从图中删去边e之后,G分 ...
- 第48套题【tarjan】【图&树的连通性】【并查集】
Problem 1 图的连通性
- 图的连通性:有向图强连通分量-Tarjan算法
参考资料:http://blog.csdn.net/lezg_bkbj/article/details/11538359 上面的资料,把强连通讲的很好很清楚,值得学习. 在一个有向图G中,若两顶点间至 ...
- 【模板】Tarjan算法与有向图的强连通性
概念 流图 给定一个有向图G= (V,E),若存在r∈V满足,满足从r出发能够到达V中所有的点,则称G是一个流图,记为(G,r),其中r是流图的源点. 流图的搜索树 在一个流图(G,r)上从r出发,进 ...
随机推荐
- EMS查看及修改邮箱发送和接受邮件大小的方法
默认情况下,新建用户邮箱没有进行单独设置,故用户邮箱默认值为"Unlimited"(未限制),即遵从全局设置(继承邮箱数据库策略).通过EMS查看用户邮箱发送和接受邮件大小的默认值 ...
- 基于express框架的留言板实现步骤
这个留言板是基于express框架,和ejs模板引擎,首先需要在根目录安装express框架,然后安装ejs模块和body-parser(获取用户表单提交的数据):建立项目目录 message,然后依 ...
- 关于Android安装apk出现解析包异常问题情况总结
原文地址:关于Android安装apk出现解析包异常问题情况总结 | Stars-One的杂货小窝 说之前,可以推荐下各位使用这个开源库AndroidUtilCode,下面提及到的工具类,都是在此库中 ...
- MySQL 数据库备份脚本
MySQL 数据库备份脚本 #!/bin/bash # 数据库连接信息 DB_HOST="127.0.0.1" DB_PORT="3306" DB_USER=& ...
- 3D 沙盒游戏之人物的点击行走移动
前言 在 3D 游戏中,都会有一个主人公.我们可以通过点击游戏中的其他位置,使游戏主人公向点击处移动. 那当我们想要实现一个"点击地面,人物移动到点击处"的功能,需要什么前置条件, ...
- 震惊!<string.h>、<cstring>和<string>竟然可以这么用!
为什么有这么多string相关的头文件呢,小编秦始皇今天带大家看一下: 1.[string.h] 定义如下:"C语言标准库中一个常用的头文件,在使用到字符数组时需要使用.[strin ...
- TCP 连接的建立 & 断开
TCP 连接的建立过程 一开始,客户端和服务端都处于 close 状态. 先是服务端监听某个端口,此时服务端处于 listen 状态. 这个时候客户端就可以发送连接请求报文了. 第一次握手 客户端会主 ...
- Git在项目中使用技巧
1.常用的命令 mkdir 文件夹名 创建文件夹 clear 清楚屏幕 ls或者ll 将当前目录下的子文件和子目录平铺在控制台 find 目录名 将对应目录下的子孙文件或子孙目录平铺在控制台 rm 文 ...
- jfinal极速开发
下载jfinal项目,上面都配置好了不用自己新建从头配置.https://jfinal.com/ idea打开项目 配置数据库 resources目录下demo-config-dev.txt # co ...
- 完爆Docker!推荐你看下这个....
现如今,互联网行业的每个人都知道数据的价值,很多人也为此学了一堆的数据分析工具,但面对问题,还是不知道如何去分析. 我们在奔向升职加薪的路上,总会遇到这些问题: 面对数据问题,没有思路,怎么办? 面对 ...