首页 > 其他分享 >【模板】边双

【模板】边双

时间:2022-10-29 09:12:11浏览次数:78  
标签:边双 int dfn low inline 分量 模板 define

这里介绍一种与众不同的写法。

前置知识:强联通分量

回忆强联通分量的算法过程,我们利用有向图的 dfn 树将图上的所有边分成四种:树边、前向边、后向边和横向边。

其中在原有树边的基础上,前向边不发挥作用,后向边一定发挥作用,横向边可能发挥作用,这个过程中我们维护了一个栈,用这个栈来确定一条边是否发挥作用(只有终点在栈里的边才会发挥作用)。

我们发现边双和强联通分量是类似的,所以可以将求强联通分量的算法拓展到无向图上,得到一个不需要求桥的边双算法。

首先一个重要的性质,在无向图除了树边就只有后向边,证明这里略过。

那么也就说明我们可以省去维护一个点是否在栈内的过程,栈内依然维护当前点的所有祖先点所属的边双中遍历过的所有点

此时因为所有边都是后向边,即所有非树边都指向自己的祖先,可以直接转移更新 low

这样就能做到甚至比普通的求强联通分量更加简单,但要注意一点,因为求的是无向图的边双,我们必须要将从父亲处过来的那条边(即树边)无视,可以直接像正常做 dfs 时那样将父亲特判掉,但因为可能出现重边,最好的办法是利用无向图相反边在链式前向星中相邻的特点直接得到需要特判掉的边的编号,如果没有重边则可以直接记录父亲编号。

你谷例题:P8436 【模板】边双连通分量

代码比先求桥再分边双简单得多(老莽の代码头略大)。

#include<bits/stdc++.h>

using namespace std;

#define Reimu inline void // 灵梦赛高
#define Marisa inline int // 魔理沙赛高
#define Sanae inline bool // 早苗赛高
#define Reisen inline LL  // 铃仙赛高

typedef long long LL;
typedef unsigned long long ULL;
typedef __int128 Suika;

inline ostream &operator<<(ostream &cout, Suika x) {
	static const LL LIM = 1e18;
	return x < LIM ? cout << LL(x) : cout << LL(x / LIM) << setw(18) << setfill('0') << LL(x % LIM);
}

typedef pair<int, int> Pii;
typedef tuple<int, int, int> Tiii;
#define fi first
#define se second

#define all(vec) vec.begin(), vec.end()
#define TY(type) const type&

template<typename Ty>
Reimu clear(Ty &x) { Ty().swap(x); }

const int N = 500010, M = 2000010;

struct Edge { int y, nx; } E[M << 1];

int n, m, eCnt, tot, cnt, top;
int lnk[N], dfn[N], low[N], stk[N];
vector<int> cc[N];

Reimu addE(int x, int y) { E[eCnt] = {y, lnk[x]}; lnk[x] = eCnt++; }

Reimu Tar(int x, int fe) {
	dfn[x] = low[x] = ++tot;
	stk[++top] = x;
	for (int e = lnk[x], y; ~e; e = E[e].nx) if (e ^ fe) {
		if (dfn[y = E[e].y]) low[x] = min(low[x], dfn[y]);
		else Tar(y, e ^ 1), low[x] = min(low[x], low[y]);
	}
	if (low[x] < dfn[x]) return;
	++cnt; do cc[cnt].emplace_back(stk[top]); while (stk[top--] ^ x);
}

int main() {
	ios::sync_with_stdio(false); cin.tie(nullptr);
	cin >> n >> m;
	memset(lnk + 1, -1, n << 2);
	for (int i = 1, x, y; i <= m; ++i) cin >> x >> y, addE(x, y), addE(y, x);
	for (int i = 1; i <= n; ++i) if (!dfn[i]) Tar(i, -1);
	cout << cnt << '\n';
	for (int i = 1; i <= cnt; ++i) {
		cout << cc[i].size() << ' ';
		for (int x: cc[i]) cout << x << ' ';
		cout << '\n';
	}
	return 0;
}

标签:边双,int,dfn,low,inline,分量,模板,define
From: https://www.cnblogs.com/LaoMang-no-blog/p/16838032.html

相关文章

  • 二分模板
    二分模板一共有两个,分别适用于不同情况。算法思路:假设目标值在闭区间[l,r]中,每次将区间长度缩小一半,当l=r时,我们就找到了目标值。版本1当我们将区间[l,r]划分成[l,m......
  • 标准模板库 02 容器vector
    容器大致分为序列式容器(Sequence),关联式容器(Associative)和无序容器(Unordered),无序容器也可以归为关联式容器内。序列式容器(Sequence)array:数组,是一种固定大小的结构,静态空......
  • 标准模板库 01 概念
    STL是可复用的标准模板库,在泛性思维上架设的一个概念结构,使抽象概念为主体,并使其系统化。容器(containers):用于存放数据,包含各种如vector,list,deque,set,map等数据结构。分配......
  • P4213 【模板】杜教筛(Sum)
    题目链接P4213【模板】杜教筛(Sum)题目描述给定一个正整数,求\[ans_1=\sum_{i=1}^n\varphi(i)\]\[ans_2=\sum_{i=1}^n\mu(i)\]输入格式本题单测试点内有多组数据。......
  • 考场Dev配置及代码模板
    编译选项:-std=c++14-O2-Wl,--stack=104857600-Wall-Wextra-Wshadow-Wl开大栈空间-Wall显示所有警告-Wextra比较始终为true或始终为false,则发出警告,但不警告常......
  • 洛谷P3391 【模板】文艺平衡树
    题目描述您需要写一种数据结构(可参考题目标题),来维护一个有序数列。其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4] 的话,结果是5 2 ......
  • 递归代码模板--分治代码模板--动态规划的关键
    递归代码模板Pythondefrecursion(level,param1,param2,...):#recursionterminator//递归终结者iflevel>MAX_LEVEL:#process_resu......
  • 利用Github Actions自动将Markdown文件转为Latex文档并生成PDF(制作一个支持自动编译
    首先放上成品的仓库地址:​​Gtihub-ACM_Template_Library​​,欢迎Star哦~效果展示:1.创建GithubActions首先创建一个GithubActions的YML文件(可以通过Github模板生成),然后......
  • 设计模式---模板方法模式
    简述提取算法中不变的部分封装成方法,变化的部分延迟到子类。延迟到子类这个说法在学习设计模式的时候经常出现,实际就是利用多态在子类中重写方法,使得实行时根据实例的......
  • P4718 【模板】Pollard-Rho算法
    题目链接P4718【模板】Pollard-Rho算法题目描述MillerRabin算法是一种高效的质数判断方法。虽然是一种不确定的质数判断法,但是在选择多种底数的情况下,正确率是可以接......