tarjan

模板 - 强连通分量 - Tarjan

白昼怎懂夜的黑 提交于 2019-11-27 11:31:53
int dfn[N], low[N], dfncnt, s[N], tp; int scc[N], sc; // 结点 i 所在 scc 的编号 int sz[N]; // 强连通 i 的大小 void tarjan(int u) { low[u] = dfn[u] = ++dfncnt, s[++tp] = u; for(int i = h[u]; i; i = e[i].nex) { const int &v = e[i].t; if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]); else if(!scc[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { ++sc; while(s[tp] != u) scc[s[tp]] = sc, sz[sc]++, --tp; scc[s[tp]] = sc, sz[sc]++, --tp; } } 来源: https://www.cnblogs.com/Yinku/p/11361532.html

P3225 [HNOI2012]矿场搭建

混江龙づ霸主 提交于 2019-11-27 11:15:48
https://www.luogu.org/problemnew/show/P3225 用Tarjan跑出割点,然后DFS搜索所有的联通快 计算每一个联通快中的割点数目 分类讨论: 如果没有割点 至少需要建立两个出口 从任意非割点的地方选择两个点建立 如果这个分组只有一个割点 只需要在分组内设立一个出口 可以设立在任意一个非割点的地方 如果有两个及以上个割点,则无需建立,可以直接到达其他联通块 1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 inline int read(){ 7 int sum=0,x=1; 8 char ch=getchar(); 9 while(ch<'0'||ch>'9'){ 10 if(ch=='-') 11 x=0; 12 ch=getchar(); 13 } 14 while(ch>='0'&&ch<='9') 15 sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar(); 16 return x?sum:-sum; 17 } 18 inline void write(int x){ 19 if(x<0) 20 putchar('-'),x=-x; 21

tarjan等

笑着哭i 提交于 2019-11-27 03:52:06
有向图注意v 在 栈中时,才用dfn更新low。无向图不用判断这个。 SCC和边双,都是在返回时判断low==dfn。 点双就是找割点,low(v)>=dfn(u)时,把tarjan(v)过程中放入栈的点以及u加入点双。 一个点可能位于多个点双。 割点为>=,割边为> 。 点双要特判根。 圆方树圆圆边就是割边。 圆方树要把边开够(n×2+m×4)。 来源: https://www.cnblogs.com/lnzwz/p/11342899.html

[POI2008]BLO-Blockade [tarjan 割点]

女生的网名这么多〃 提交于 2019-11-27 03:33:32
P3469 [POI2008]BLO-Blockade 分为两种情况 一种不为割点时贡献为2*(n-1) 为割点时贡献为各个连通块之间互相的贡献 开始无法理解 ans[u]+=(ll)sum*sz[v],sum+= sz[v] 是如何求出贡献的 可以发现在访问该割点下面的各个块时像这样相乘 就把各个块之间相乘的值都计算进去了(==好吧还是自己仔细理解 描述不出来) 然后就是为无向要*2 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define Max(x,y) ((x)>(y)?(x):(y)) #define Min(x,y) ((x)<(y)?(x):(y)) #define ll long long const int N=1e5+5,M=5e5+5,INF=1e9+7,inf=0x3f3f3f3f; int n,m; ll ans[N]; template <class t>void rd(t &x){ x=0;int w=0;char ch=0; while(!isdigit(ch)) w|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x

最近公共祖先(LCA)的Tarjan算法

一曲冷凌霜 提交于 2019-11-27 00:50:23
最近公共祖先(LCA)问题 LCA(T,u,v):在有根树T中,询问一个距离根最远的结点x,使得x同时为结点u、v的祖先 LCA问题可以用朴素的DFS方法解决,但是时间复杂度就很高了,这里介绍一种高级一点的解决LCA问题的Tarjan算法。 Tarjan算法是由 Robert Tarjan 在1979年发现的一种高效的离线算法,也就是说,它要首先读入所有的询问(求一次LCA叫做一次询问),然后并不一定按照原来的顺序处理这些询问。 首先需要有一些预备知识: 1.基本图论 这个就不多讲了,如果有不知道的可以随便抓一本数据结构的书恶补一下。 2.并查集 并查集其实也是很简单的东西,实现的代码都不超过10行。 这里提一下并查集的概念,并查集是一种处理元素之间等价关系的数据结构,一开始我们假设元素都是分别属于一个独立的集合里的,主要支持两种操作: 合并两个不相交集合(Union) 判断两个元素是否属于同一集合(Find) 需要知道一点,就是并查集的Find操作的时间复杂度是常数级别的。 考察树 T 中所有与结点 u 有关的询问 (u, v) 对于子树 u 中的结点 v ,满足 LCA(u, v) = u 对于子树 p1 而非子树 u 中的结点 v ,满足 LCA(u, v) = p1 对于子树 p2 而非子树 p1 中的结点 v ,满足 LCA(u, v) = p2 算法DFS有根树T

强联通分量,缩点

梦想与她 提交于 2019-11-27 00:29:45
没有用的话qaq : Ummmm…图论的大部分知识本来早就有学过,只是一直没有写成博文来梳理,但既然上了qbxt DP图论就写一篇来总结下, 主要是来听DP的,但…由于太菜的原因,DP听得天花乱坠QWQ 一,基础知识篇 1,强连通:如果有两个点u和v,如果u能到达v,v也能到达u,就称u和v是强连通的。 2,强连通分量:有向图极大的强连通子图。 3,强连通图:有向图中任意两个顶点都强连通的图称为强连通图。 4,缩点:将所有的强连通分量都缩成一个点,有向图缩点会形成一个DAG 比如上图中,1和4可以互相到达,我们称1和4强连通。 1,2,3,4构成了一个极大强连通子图(即不存在一个包含1,2,3,4但仍比1,2,3,4构成的强连通子图还大的图),1,2,3,4构成一个强连通分量。 二,找寻强连通分量的方法 1,Kosaraju算法 :基于两次DFS的有向图强连通子图算法。 算法的主要思想是建一个正着的图,再建一个反着的图,先dfs遍历正向图,记下完成遍历的顺序,记住是完成遍历的顺序而不是遍历的顺序,比如上图 遍历的顺序是1-2-4-6-3-5 完成遍历的顺序是 6-4-2-5-3-1,然后再用返图按完成遍历的顺序取最后一个完成访问的节点开始dfs,每dfs一遍证明有一个强连通分量。 一般求取强连通分量运用下一种方法较多,此种方法博主认为OI中应用较少 代码实现 inline void

Tarjan全家桶

我只是一个虾纸丫 提交于 2019-11-26 22:34:59
有向图强连通分量 030 #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int N = 10010; const int M = 10010; int n, m, top, num, tot = 0, sum = 0, st[N], last[M], to[M], dfn[N], low[N], vis[N], z[N]; void tarjan(int u) { dfn[u] = low[u] = ++tot; z[++top] = u; vis[u] = 1; for (int i = st[u]; i ; i = last[i]) { if (!dfn[to[i]]) { tarjan(to[i]); low[u] = min(low[u], low[to[i]]); } else if (vis[to[i]]) low[u] = min(low[u], low[to[i]]); } if (dfn[u] == low[u]) { do { printf("%d ", z[top]); vis[z[top]] = 0; top--; }while (u != z[top + 1]); printf("\n"); } }

tarjan模板完整版

淺唱寂寞╮ 提交于 2019-11-26 22:31:26
https://www.luogu.org/problem/P2863 #include<cstdio> #include<vector> using namespace std; int dfn[10005],low[10005],stack[10005],scc[10005],num[10005],vis[10005]; int clock,scc_cnt,top; vector<int>e[10005]; inline void dfs_scc(int x) { dfn[x]=low[x]=++clock;//访问次序标记;x能到的祖先中节点编号最小的 stack[++top]=x;//把走过的节点入栈 vis[x]=1; for(int i=0;i<e[x].size();i++) { int now=e[x][i]; if(!dfn[now])//如果没有被访问过 { dfs_scc(now);//进它 low[x]=min(low[x],low[now]);//既然now是x的子节点,那么他们一定有公告的祖先,取个小的 } else if(vis[now])//如果他还没有被其他强连通分量使用 low[x]=min(low[x],dfn[now]);//那么再小一点 } if(low[x]==dfn[x]) { scc_cnt++; while(stack[top]!

轰炸行动:tarjan,拓扑排序

二次信任 提交于 2019-11-26 19:41:23
考场上看错题,没什么好说的。 然而它就是一个大板子。 发的题解勉强还能看。但是我还想再讲讲。 题目的表述是,如果从A能直接或间接到B,那么就不能同时轰炸A和B。 那么我们从图里随便拽出一条有向路径,从这条路径中随意挑2个点AB,那么要么能从A到B要么从B到A 那么你任意挑出的这两个点 只要不是同一个点 那么就不能同时轰炸。 带下划线的那一段有什么用呢?它的正确性是显然的。 我所说的“有向路径”没有加任何附加限制,所以可以包括环,环上转几圈就可能出现同一个点呗。 我们考虑单纯的一个环。那么它上的每一个都要单独炸一次。 再考虑单纯的一条路径,那么路径上的每一个点也需要单独炸一次。 如果一个路径进入了一个环,那么这上面的点也必须单独炸一次(路径上的点可以到达环上的任意点)。 如果一个环引出了一个路径,那么环上的点亦可到路径上,都要单独炸一次。 综上,就是要找出一条路径使它上面的不同的点尽量多,不同的点的个数即为答案。 上面那一堆话里已经包括了这个意思:环上的每个点都会给路径长度增加1,且对联通性没有影响。 所以考虑tarjan缩完强联通分量后就没有环了,只不过环变成了权值等于环中点数的一个大点而已 其余普通点的权值为1。现在的问题就变成了在一个DAG里找一条路径使它上面的点权和最大。 不能dfs,因为这是有向的DAG,虽然不是环但也不是树,它可以长得很恶心。

Tarjan缩点入门

谁都会走 提交于 2019-11-26 16:04:40
缩点 顾名思义,缩点就是把一个强连通分量缩成一个点 Tarjan 在dfs的过程中记录时间戳,若能够通过某个点返回已遍历的点,则可以缩点 inline void Tarjan(int x)// st栈,low当前可已过的时间戳最大的,dfn当前点的时间戳,co当前点属于的强连通分量 { low[x]=dfn[x]=++cnt; st[++top]=x,vis[x]=1; for(re int i=h[x];i;i=e[i].ne) { int y=e[i].to; if(!dfn[y]) { Tarjan(y); low[x]=min(low[x],low[y]); } else if(vis[y]) low[x]=min(low[x],dfn[y]); } if(low[x]==dfn[x]) { ++color; while(st[top+1]!=x) { co[st[top]]=color; ww[color]+=w[st[top]]; vv[color]+=v[st[top]]; vis[st[top--]]=0; } } } 来源: https://www.cnblogs.com/yzhx/p/11325586.html