tarjan

tarjan求强连通分量(模板)

蹲街弑〆低调 提交于 2019-11-28 07:18:09
https://www.luogu.org/problem/P2341 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=50010; int pre[maxn],other[maxn],last[maxn],l; int n,m; int dfn[maxn],low[maxn],ans[maxn],st[maxn],belong[maxn],cnt,top,qw; //dfn->dfs序,low是点上非树边指向的点(拥有最小的dfs序 ),st是一个栈,记录环上的点,belong是点所属于的环 void add(int x,int y) { l++; pre[l]=last[x]; last[x]=l; other[l]=y; } int ru[maxn],chu[maxn];//入度,出度 void dfs(int x) { dfn[x]=low[x]=++cnt;//可以知道每个点都指向自己(low) ru[x]=1; st[++top]=x; for(int p=last[x];p;p=pre[p]) { int v=other[p]; if(!dfn[v]) { dfs(v);//此时v的信息已经更新完毕 low[x]=min(low[x]

图的割点、桥与双连通分支

可紊 提交于 2019-11-28 06:44:52
先来%一下Robert Tarjan前辈 %%%%%%%%%%%%%%%%%% 然后是热情感谢下列并不止这些大佬的博客: 图连通性(一):Tarjan算法求解有向图强连通分量 图连通性(二):Tarjan算法求解割点/桥/双连通分量/LCA 初探tarjan算法(求强连通分量) 关于Tarjan算法求点双连通分量 图的割点、桥与双连通分支 感谢有各位大佬的博客帮助我理解和学习,接下来就是进入正题。 关于tarjan,之前我写过一个是求lca的随笔,而找lca只是它一个小小的功能,它还有很多其他功能,具体是什么,我就根据自己的学习进程,一步步来自我回忆一下。 第一个是有向图求强连通分量。 让我们来引进百度 有向图强连通分量:在 有向图 G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点 强连通 (strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个 强连通图 。有向图的极大强连通子图,称为强连通 分量 (strongly connected components)。 通俗解释,强连通就是有向图中,任意两个顶点彼此有一条路可以可以走到,而强连通分量就是它的强连通子图,极大强连通分量就是它最大的那个强连通子图。 而详细步骤的话,上面的博客已经讲得很清楚了,我就简单的阐述一下步骤。

LCA详解

a 夏天 提交于 2019-11-27 21:02:14
LCA,即最近公共祖先,在图论中应用比较广泛。 LCA的定义如下:给定一个有根树,若节点$z$同时是节点$x$和节点$y$的祖先,则称$z$是$x,y$的公共祖先;在$x,y$的所有公共祖先当中深度最大的称为$x,y$的最近公共祖先。下面给出三个最近公共祖先的例子: 显然,从上面的例子可以得出,$LCA(x,y)$即为$x,y$到根节点的路径的交汇点,也是$x$到$y$的路径上深度最小的节点。 求LCA的方法通常有三种: 向上标记法 树上倍增法 Tarjan算法 当然,求LCA还有其它方法,例如树剖等,请读者自行了解,本文主要讲解上面提到的三种方法。 向上标记法 向上标记法是求LCA最直接的方法,直接根据定义来求,单次查询的时间复杂度最坏为$O(n)$ (看起来好像还挺快的,不过什么题会只有一次查询呢) 算法流程: 从x节点向上一直走到根节点,沿途标记经过的节点 从y节点向上一直走到根节点,当第一次遇到已标记的节点时,该节点就是$LCA(x,y)$ 该方法思想是绝对简单的,实现也简单,因此在这里就不给出具体实现了。不过由于其时间复杂度过高,在实际中基本不会应用到,在这里提一下主要还是为讲解Tarjan算法做基础。 树上倍增法 树上倍增法应用非常广泛,读者可以深入地学习。用树上倍增法求LCA的时间复杂度为$O((n+m)logn)$。 树上倍增法用到了二进制拆分的思想。在求LCA时

[模板]tarjan——最后通牒

雨燕双飞 提交于 2019-11-27 19:10:16
这么久了我还是不会板子,你们随便笑话我吧。 再不会打我实在是无能为力了。 这篇博客写的像个智障一样。。。写它的目的就是自嘲? 才不是,为了方便查阅,因为我真的记不住。 对于割边,要存储该点入边的编号,因为更新low时不能沿着反向边爬回去。 遍历没走过的儿子时判定:如果儿子的low大于该点的dfn,则两点之间的路为割边。 对于割点,表达式为low[son]>=dfn[father].而对于根节点,必须有至少2个儿子满足条件时才能说根是割点。 从一个节点扫到的所有点都可以更新low值。 边双,就是割掉所有的割边之后图中剩下的联通分量。 缩点时把所有在同一个edcc里的合并,用割边连边即可。得到一棵树。 点双,dfs时使点入栈,在判割点时一旦满足条件,弹栈直到这个儿子弹出,再把父节点加入构成vdcc。 但是对于根节点是不是割点的判定没有变化。 缩点时,因为一个割点可能包含于多个vdcc,所以把割点作为中转站,把割点与其所在的vdcc连边,得到一棵树。 对于有向图强联通分量,也是维护了一个栈,dfs到点时加入。 搜索到在栈里的点(祖先)时更新low。如果回溯前dfn==low那么不断弹栈直到本节点弹出构成一个scc。 剩下的边会把图变为DAG,是有向无环图,接下来要拓扑排序而不是dfs!! 来源: https://www.cnblogs.com/hzoi-DeepinC/p

tarjan 割顶和桥 学习笔记

Deadly 提交于 2019-11-27 18:25:12
无向图的割顶和桥 low[i] 表示 i号节点能访问到最早节点的编号 dfn[i] 表示 i号节点在dfs序中的编号 void tarjan(int u,int fa){ int v; int child = 0,k = 0; dfn[u] = low[u] = ++dfs_clock; for(int i=head[u];i!=-1;i=edge[i].nxt){ v = edge[i].to; if(v == fa && !k){ // 处理重边 第一次访问到父亲则不访问 k++; continue; } if(!dfn[v]){ // v是u的儿子 child++; tarjan(v,u); low[u] = min(low[u],low[v]); // 儿子能访问到,父亲也能访问到 if(low[v] > dfn[u]){ // 儿子不能访问到父亲前面的点 // is_cut[i] = true; // i号边为割边 } if(low[v] >= dfn[u] && fa!=-1){ // 非树根且儿子不能访问他前面的点 iscutpoint[u] = true; } }else{ // v已经被访问过, 即u可以绕过父亲来访问更靠前的点 low[u] = min(low[u],dfn[v]); } } if(fa == -1 && child>1 ){ //

2-SAT入门

佐手、 提交于 2019-11-27 16:55:44
SAT是适定性(Satisfiability)问题的简称。一般形式为k-适定性问题,简称 k-SAT。而当$k>2$ 时该问题为NP完全的。所以我们只研究$k=2$时 情况。 定义 2-SAT,简单的说就是给出$n$ 个集合,每个集合有两个元素,已知若干个$\langle a, b \rangle$ ,表示$a$与$b$ 矛盾(其中$a$ 与$b$ 属于不同的集合)。从每个集合选择一个元素,使得选出的$n$ 个元素两两不矛盾的问题,就是2-SAT问题。显然可能有多种选择方案,一般题中只需要求出一种即可。 解决方法 我们考虑将2-SAT问题往图论的方向靠,每个元素用结点来表示,元素间的矛盾关系建边来表示。 设$\{a, a'\}$是一个集合里的元素,$\{b, b'\}$是另一个集合里的元素,假如$a,b$之间有矛盾,那么连边$a\to b'$,表示选了$a$就要选$b'$,连边$b\to a'$,表示选了$b'$就要选$a$。 这样问题就转化为了从$2\times n$个结点里选出$n$个分属不同集合的结点,并且对于每个选出的结点,其所有后继结点也都被选出。 方法1:dfs暴搜 就是沿着图上一条路径,如果一个点被选择了,那么这条路径以后的点都将被选择,那么,出现不可行的情况就是,存在一个集合中两者都被选择了。最坏时间复杂度为$O(nm)$,虽然后面讲的Tarjan

POJ 3177 Redundant Paths (tarjan无向图求缩点)

别说谁变了你拦得住时间么 提交于 2019-11-27 15:15:48
#include <iostream> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<cstdio> #include<stack> #include <map> #define lson(p) (p<<1) #define rson(p) (p<<1|1) #define ll long long using namespace std; const int N = 1e5+10; int n,m; struct Edge{ int to,nxt; }edge[N<<2]; int tot,head[N]; int dfn[N],low[N],fa[N],d[N]; stack<int> st; int ind,ans; void init(){ tot = 0; memset(head,-1,sizeof head); memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); } void add(int u,int v){ edge[tot].to = v; edge[tot].nxt = head[u]; head[u] = tot++; } void tarjan(int u,int root){ int v,sg

[树形dp][Tarjan][单调队列] Bzoj 1023 cactus仙人掌图

岁酱吖の 提交于 2019-11-27 14:59:35
Description   如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌 图(cactus)。所谓简单回路就是指在图上不重复经过任何一个顶点的回路。   举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(4,3,2,1,6 ,5,4)、(7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同时出现在前两 个的简单回路里。另外,第三张图也不是仙人图,因为它并不是连通图。显然,仙人图上的每条边,或者是这张仙 人图的桥(bridge),或者在且仅在一个简单回路里,两者必居其一。定义在图上两点之间的距离为这两点之间最 短路径的距离。定义一个图的直径为这张图相距最远的两个点的距离。现在我们假定仙人图的每条边的权值都是1 ,你的任务是求出给定的仙人图的直径。 题解 如果是圆圆边的话和普通的树形dp一样 对于环的话,把他拎出来单独考虑,首先要计算出这个环的贡献,然后更新环的最顶点 更新的话,就这个直接拿环上的点的dp值 ,再计算一下这两点之间的最短路 ,相加后更新 贡献的话,就是max(f[i]+f[j]+dis(i,j)),dis(i,j)=min(abs(deep[i]-deep[j]),size[环]-abs(deep[i]-deep[j]))

[树形dp][Tarjan] Bzoj 4316 小C的独立集

大憨熊 提交于 2019-11-27 14:49:21
Description 图论小王子小C经常虐菜,特别是在图论方面,经常把小D虐得很惨很惨。 这不,小C让小D去求一个无向图的最大独立集,通俗地讲就是:在无向图中选出若干个点,这些点互相没有边连接,并使取出的点尽量多。 小D虽然图论很弱,但是也知道无向图最大独立集是npc,但是小C很仁慈的给了一个很有特点的图: 图中任何一条边属于且仅属于一个简单环,图中没有重边和自环。小C说这样就会比较水了。 小D觉得这个题目很有趣,就交给你了,相信你一定可以解出来的。 题解 可以考虑用仙人掌的做法来弄,我们可以不用把圆方树给建出来 直接在tarjan中做,设f[i][0/1]表示当前点为i选或不选i的子树的最大独立集 转移的话和一般的树形dp差不多,这样子做完就相当于把环给单独拎出来考虑 代码 1 #include <cstdio> 2 #include <iostream> 3 #define N 60010 4 using namespace std; 5 struct edge{int to,from;}e[N*3]; 6 int n,m,cnt=1,Fa[N],f[N][2],dfn[N],low[N],head[N]; 7 void insert(int x,int y) 8 { 9 e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; 10 e[

洛谷 - P3469 - BLO-Blockade - 割点

空扰寡人 提交于 2019-11-27 11:56:54
https://www.luogu.org/problem/P3469 翻译:一个原本连通的无向图,可以删除图中的一个点,求因为删除这个点所导致的不连通的有序点对的数量。或者说,删去这个点之后,各个连通分量的大小的乘积之和? 当然是考虑新学的Tarjan法求割点。一遍Tarjan给每个点记录他是不是割点。然后第二遍的时候对每个割点,统一它连接的 来源: https://www.cnblogs.com/Yinku/p/11361600.html