首页 > 其他分享 >学图论

学图论

时间:2024-11-13 19:07:25浏览次数:1  
标签:图论 int 短路 路径 连通 支配 欧拉

Boruvka

每一轮操作,对于每个点来说,让他和“最近的与他有连边且还未连通的点”相连。

最多 \(\log n\) 轮,每轮 \(O(n\cdot p)\),\(p\) 为找“最近的与他有连边且还未连通的点”的复杂度。

\(O(np\log n)\)

Kruskal 重构树

设从小到大加边,性质:

  • 二叉树,\(2n-1\) 个点(数组开两倍)
  • 原图的节点对应树中的叶子
  • 若忽略叶子,重构树满足堆的性质,即父亲 \(\ge\) 儿子
  • \(u,v\) 之间所有路径的“经过边权最大值”的最小值,是 \(u,v\) 在重构树上 LCA 的权值
  • 原图 \(u\) 点,其只经过 \(\le w\) 的边能到达的点,为 \(u\) 在树上最高的权值 \(\le w\) 的祖先的子树内所有叶子,找到这个点可倍增。

他的性质比原图最小生成树的性质,要好得多。

同余最短路

问题:给定一些数 \(a_i\),求某范围内的数有多少数可用 \(a_i\) 完全背包得出。

若 \(r\) 能被表示出,则 \(r+k\cdot a_i\) 也能被表示出,故固定一个 \(a_t=m\) 作为模数,对 \(x\in[0..m-1]\) 算最小的能被凑出的 \(p\equiv x\pmod m\) 是多少。

建图:对所有 \(i\),\(x\to(x+a_i)\bmod m\) 连权值为 \(a_i\) 的边,跑最短路即可得出所求。

转圈做法:考虑模 \(m\) 意义下的完全背包,考虑新加入一个 \(a_i\),显然他在长度为 \(m\) 的环上形成 \(\gcd(a_i,m)=d\) 个子环,对每个子环分别顺序转移两圈即可;或者从子环上权值最小的点开始顺序转移一圈即可(写起来不如前者简洁)。\(O(nm)\)

个人觉得转圈做法更自然。

Johnson

多源最短路,支持负边,\(O(nm\log m)\)

考虑对每个点 \(i\) 赋 \(h_i\),令 \(w'(u,v)\gets w(u,v)+h_u-h_v\),求出新图 \(s\to t\) 最短路后减去 \(h_s-h_t\) 即可求出原图最短路。

列出条件:\(w(u,v)+h_u-h_v\ge 0\),即 \(h_u+w(u,v)\ge h_v\),发现令 \(h_u\) 为 \(d_u\) 即可。

故令 \(w(S,i)=0\),跑 SPFA(顺便判负环),改边权得新图后跑 \(n\) 次 Dijkstra 即可。

最短路树

若 \(v\) 的最短路是由 \((u,v)\) 松弛而来,则在生成树中保留 \((u,v)\)。

性质:树上 \(s\to v\) 的路径长度为 \(s\to v\) 的最短路。

最短路图

若 \((u,v,w)\) 满足 \(dis_u+w=dis_v\),则保留这条边。

相当于对每个点 \(v\) 保留图上所有 \(s\to v\) 的最短路。

性质:

  • 按 \(dis\) 定向后是个 DAG
  • 他的所有生成树都是最短路树

若 \(dis(s,u)+w+dis(v,t)=dis(s,t)\),则 \((u,v,w)\) 可以在 \(s\to t\) 的最短路上。

欧拉回路

看代码就懂了。

int tp, stk[N];
void Eul(int u) {
	for (int &i = cur[u]; i < (int)G[u].size(); ) Eul(G[u][i++]);
	stk[++tp] = u;
}

Eul(s);
while (tp) cout << stk[tp--] << ' ';
cout << '\n';
  • 欧拉路径:一笔画
  • 欧拉回路:一笔画,起点等于终点
  • 欧拉图:存在欧拉回路
  • 半欧拉图:存在欧拉路径,但不是欧拉图

欧拉图判定:

  • 有向图:弱连通,入度等于出度
  • 无向图:连通,每个点度数为偶数

半欧拉图判定:

  • 有向图:弱连通,存在两个点入度分别等于出度加一、出度减一,其余点入度等于出度
  • 无向图:连通,恰好两个点度数为奇数

连通性

\(\text{low}(u)\):\(u\) 子树内能一步走到的最小 dfn

  • 边双:割边,每个点恰属于一个边双连通分量
  • 点双:割点,每条边恰属于一个点双连通分量

Menger 定理:

  • 边形式:使 \(u,v\) 不连通所需删去的边数最小值,等于 \(u,v\) 之间边不交的路径的数量的最大值。
  • 点形式:使 \(u,v\)(不相邻)不连通所需删去的点数最小值,等于 \(u,v\) 之间点不交(不含 \(u,v\))的路径的数量的最大值。

证明可以看 Alex-Wei 的。这个定理与最大流-最小割定理类似。

于是我们可以定义:

  • 局部边连通度:使 \(u,v\) 不连通所需删去的边数最小值;
  • 局部点连通度:使 \(u,v\) 不连通所需删去的点数最小值(\(u,v\) 不相邻);
  • \(k\)-边连通:\(u,v\) 边连通度 \(\ge k\);
  • \(k\)-点连通:\(u,v\) 点连通度 \(\ge k\)。

圆方树

普通圆方树是只支持仙人掌的;这里默认的圆方树是广义圆方树。

建法:若 \(\text{low}(v)\ge\text{dfn}(u)\),说明 \(v\) 子树在栈内的部分与 \(u\) 形成了点双,新建方点并连边、弹栈直到 \(v\) 被弹出即可,注意两倍空间。

圆方树完整保留了原图的必经性。

支配树

对于有向图,若 \(s\to y\) 的所有路径均经过 \(x\),则称 \(x\) 支配 \(y\)。

由于支配关系具有

  • 传递性:若 \(x\) 支配 \(y\)、\(y\) 支配 \(z\),则 \(x\) 支配 \(z\)。
  • 自反性:\(x\) 支配 \(x\)。
  • 反对称性:若 \(x\) 支配 \(y\),\(y\) 支配 \(x\),则 \(x=y\)。

这保证了支配是偏序关系,这让他能建出 DAG,但不够优秀。

性质:若 \(x,y\) 均支配 \(z\),则 \(x,y\) 之间也有支配关系。

注意到这样就可以建树了:考虑支配 \(y\) 的所有点 \(D_y=\{x_1,x_2,\ldots,x_k\}\),则 \(D_y\) 内所有元素形成了全序关系,即任意两个元素之间均可比较,所有 \(x_i\) 的关系可以用一条链描述,这就是 \(y\) 的祖先链。

令 \(f(i)\) 表示 \(D_i\) 当中被其他所有点支配的 \(x_j\),在 \(i\) 和 \(f(i)\) 之间连边,这就是支配树。

支配树用于描述有向图在给定起点时节点的必经性。

对于 DAG,我们可以 \(O((n+m)\log n)\) 求出其支配树:

按拓扑序处理,若 \(x\) 有入边 \(y_i\to x\),则

\[ f(x)=LCA(y_1,y_2,\ldots,y_k) \]

动态更新倍增数组即可。

DAG 链剖分

若原图有若干无入度点,新建 \(1\) 向他们连边,这样只有 \(1\) 没有入度。

设 \(f(i)\) 为从 \(i\) 出发的路径条数。定义重儿子为 \(f\) 值最大的后继,这样对于任意轻边 \(i\to j\),\(2f(j)\le f(i)\)。这可以解决部分题目,但不够优秀,我们想要 \(i\to son(i)\) 形成的是链而不是树。

设 \(g(i)\) 为 \(1\to i\) 的路径条数。设 \(i\to j\) 为重边当且仅当 \(j\) 为 \(i\) 的 \(f\) 值最大的后继,且 \(i\) 为 \(j\) 的 \(g\) 值最大的前驱。这样形成了若干条链。走一条轻边,要么 \(f\) 减半,要么 \(g\) 翻倍,故一条路径上的轻边条数为 \(\log V\) 级别,\(V\) 为总路径条数。这说明只有当路径条数不大时 DAG 链剖分才适用

DAG 链剖分常与 SAM 结合,因为此时路径条数为 \(s\) 的本质不同子串数,为 \(O(n^2)\) 级别,且剖分后可以快速定位字符串查询相关信息。更重要的是可以通过 \(\log\) 条时间戳连续的链描述 \(s[l,r_1]\) 到 \(s[l,r_2]\) 的路径,非常有用。

Dilworth 定理

一个偏序集的最大反链大小等于其最小链覆盖。

考虑一个长度为 \(n\) 的排列 \(p\)。以下两个结论至少有一个成立:

  • 存在一个长度为 \(\sqrt n\) 的递增序列
  • 存在一个长度为 \(\sqrt n\) 的递减序列

Hall 定理

设一个二分图的两部点数为 \((x,y),x<y\),其的一个完美匹配定义为左部 \(x\) 个点成为匹配点。

该二分图存在完美匹配的充要条件是,对于左部点的大小为 \(k\) 的任意子集 \(S\),这些点在右部点连到的点集(也被称为 \(S\) 的邻域,记为 \(N(S)\))大小不小于 \(k\)。

推论:一个任意二分图 \((V,E)\) 的最大匹配为

\[ |V|-\max_{S\subseteq V}\{|S|-|N(S)|\} \]


习题会添加在这里面,附带简要题解。

题目选做

1. CF1965F Conference

Statement

二分图匹配,但是左边每个人连向的都是一段区间,\(\forall k\in[1..n_1]\),输出从右边点选任意长度为 \(k\) 的区间和左边所有人做匹配,匹配数能达到 \(k\) 的区间的个数,\(n_1,n_2\le2\cdot10^5\).

Solution

判断右边点单个区间 \([l,r]\),考虑 Hall 定理,设 \(N(S)\) 为右边点 \(S\) 对应的左边点集大小,\(S\) 合法当且仅当 \(|N(S)|\ge|S|\),那么 \([l,r]\) 合法当且仅当 \(\forall S\subseteq[l,r],S\) 合法.

猜测只用判断成一段区间的 \(S\),但是会被以下数据叉掉:

3
1 3
2 2
2 2

对于 \([1,3]\),只有 \(\{1,3\}\) 不合法.

于是考虑处理初始的左边点的线段,发现对于 \([a,b],[a,c](b\le c)\),把他们换成 \([a,b],[a+1,c]\) 不影响结果(\(a=b=c\) 时相当于删掉这条线段),因为对于 \(a\) 点,为了最优,他一定会选 \([a,b]\) 而非 \([a,c]\).

处理后每个人的左端点互不相同,此时找性质,发现:

  • 可以只判成一段区间的 \(S\),因为若一个不合法的 \(S\) 不是一段连续的区间,考虑将其补全成一段区间,此时他还是不合法,因为每补全一个数,\(|N(S)|\) 至多增加 \(1\),此时有只包含这个数的区间.
  • 看单调性,发现若 \([l,r]\) 不合法,\([l,r+1],[l-1,r]\) 也不合法,因为 \([l,r]\) 合法当且仅当 \(\forall S\subseteq[l,r],S\) 合法.

于是可以双指针求,对每个 \(l\) 最大的 \(r\) 使 \([l,r]\) 合法,最后差分统计即可.

Code
// LUOGU_RID: 160078999
// by:Joey_c / sto hunction orz
#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define reo(i,j,k) for(int i=j;i>=k;i--)
#define debug fprintf(stderr,"Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define fi first
#define se second
#define pb emplace_back
#define mp make_pair
typedef long long ll;
constexpr int N=2e5+10;

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	int n,m=0,nn;cin>>n,nn=n;vector<pair<int,int>>a(n+1);vector<int>buc[N];rep(i,1,n)cin>>a[i].fi>>a[i].se,m=max(m,a[i].se),buc[a[i].fi].pb(a[i].se);
	n=0;priority_queue<int,vector<int>,greater<int>>q;
	rep(i,1,m){
		for(int j:buc[i])q.push(j);while(!q.empty()&&q.top()<i)q.pop();if(!q.empty())a[++n]=mp(i,q.top()),q.pop();
	}
	vector<int>b(m+2,0),c(m+2,0),ans(max(m,nn)+2,0);rep(i,1,n)b[a[i].fi]=1,++c[a[i].se];
	for(int s=0,l=m,r=m;l;--l){
		s+=c[l];while(r>=l&&s<r-l+1)s-=b[r--];if(r<=m)++ans[r-l+1];
	}reo(i,max(m,nn),1)ans[i]+=ans[i+1];rep(i,1,nn)cout<<ans[i]<<"\n";
	return 0;
}

标签:图论,int,短路,路径,连通,支配,欧拉
From: https://www.cnblogs.com/laijinyi/p/18544589

相关文章

  • 算法-图论-拓扑排序
    1.拓扑排序(卡码网117)fromcollectionsimportdeque,defaultdictdefmain():num_node,num_edge=map(int,input().split())inDegrees=[0for_inrange(num_node)]edges=defaultdict(list)for_inrange(num_edge):source,target=......
  • 图论基础
    图论基础图的存储图的遍历最小生成树kruskal算法prim算法最短路Dijkstra算法Bellman-Ford算法SPFA算法Floyd-Warshall算法图论基础拓扑排序一笔画问题关键路径差分约束基环树DAG边集数组采用结构体存储边,包括边的起点、终点、权值等信息,这个结......
  • LUOGU_图论
    LUOGU_图论ST表+DFN序LCA每次在自己的DFN序位置放入自己的父亲询问的时候l+1ST表+欧拉序LCA\(u,v\)在欧拉序中的第一个位置之间的深度最小位置就是LCA树的直径相距最远的两个点\(\max_{u,v}dis(u,v)=\max_{u,v}(dep_u+dep_v-2dep_{lca(u,v)})\)边权非负:两次BFS边权有......
  • 基于图论的时间序列数据平稳性与连通性分析:利用图形、数学和 Python 揭示时间序列数据
    时间序列数据表示了一个随时间记录的值的序列。理解这些序列内部的关系,尤其是在多元或复杂的时间序列数据中,不仅仅局限于随时间绘制数据点(这并不是说这种做法不好)。通过将时间序列数据转换为图,我们可以揭示数据片段内部隐藏的连接、模式和关系,帮助我们发现平稳性和时间连通......
  • 算法学习笔记3:图论
    图论拓扑序列有向无环图一定存在拓扑序列,通过入度为0来判断该点是否可以加入队列。强连通分量定义:在有向图G中,如果两个顶点u,v间有一条从u到v的有向路径,同时还有一条从v到u的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向非强连通图......
  • 《 C++ 修炼全景指南:十七 》彻底攻克图论!轻松解锁最短路径、生成树与高效图算法
    摘要1、引言1.1、什么是图?图(Graph)是计算机科学和离散数学中一种重要的数据结构,用来表示一组对象之间的关系。一个图由顶点(也称为节点,Vertex)和边(Edge)组成。顶点表示实体,而边则表示实体之间的关联或连接关系。根据边的性质,图可以分为无向图和有向图。在无向图中,边没有方向......
  • 算法汇总整理篇——回溯与图论的千丝万缕及问题的抽象思考
    回溯算法(重中之重)回溯法解决的问题都可以抽象为树形结构,集合的大小就构成了树的广度,递归的深度就构成了树的深度。(回溯的核心:分清楚什么数据作为广度,什么数据作为深度!!!!!)voidbacktracking(参数){if(终止条件){存放结果;return;}for......
  • 【代码随想录Day54】图论Part06
    冗余连接题目链接/文章讲解:代码随想录importjava.util.Scanner;publicclassMain{privateintnumberOfNodes;//节点数量privateint[]parent;//存储每个节点的父节点//构造函数初始化并查集publicMain(intsize){numberOfNod......
  • 【代码随想录Day53】图论Part05
    并查集理论基础题目链接/文章讲解:并查集理论基础|代码随想录寻找存在的路径题目链接/文章讲解:代码随想录importjava.util.*;publicclassMain{publicstaticvoidmain(String[]args){intnumberOfElements,numberOfConnections;Scann......
  • 【代码随想录Day52】图论Part04
    字符串接龙题目链接/文章讲解:代码随想录importjava.util.*;publicclassMain{//使用广度优先搜索(BFS)方法计算从beginWord到endWord的最短转换序列长度publicstaticintfindLadderLength(StringbeginWord,StringendWord,List<String>wordList){......