【学习笔记】图论杂谈(三)

懒得整了,有时间再来整

1.图论经典问题

最小点覆盖:覆盖边

最小支配集:覆盖点

最大独立集:

2.最短路

二进制分组最短路

差分约束:最长路求最小值,最短路求最大值

3.连通性

tarjan的dfn序是逆拓扑序,枚举即可

边的判重方式:哈希表记录每个点的前驱节点,如果是u则跳过,否则更新u

有的题目需要记录割点割边,即它们在后续起到作用 P3225

4.有向图博弈

P9169 [省选联考 2023] 过河卒

建反边,从末态开始算01SG函数

5.生成树

瓶颈生成树

最大生成树

瓶颈最短路:kruskal生成树

\[QWQ \]

6.状态图

状态图P5100

7.查分约束

最短路:考虑 \(x_0=0\) ,解存在, \(0\) 出发能达到 \(v\)\(dis_0=0\),那么 \(dis_v\) 就是 \(x_v\) 的最大值。
最长路:反之

8.TWO-SAT

N-SAT NPC问题

蕴含图(满足原图时同时满足的图)

9.优化建图

前后缀优化建图CF587D:一个点代表一个前后缀

线段树优化建图

10.Tarjan优化LCA

(u,v)
如果祖宗关系:find(v)=v
非祖宗关系:find(v)=lcd(u,v)(未合并)

11.BEST定理:计算有向欧拉回路数量

引理 1 (最后离开树)

给定一条欧拉回路。对每个 𝑣 ≠ 𝑠,取最
后一次离开 𝑣 的边。它们构成一棵以 𝑠 为根的内向树。

定理 2 (BEST 定理)

从固定边开始的欧拉回路数量为 \(r_s ∏_{v∈V}(outdeg(v) − 1)!\)

矩阵树定理求 \(r_s\)

12.最小树形图

首先给出树形图的定义:(可以近似理解为有向图上的生成树)

  • 有向图中定义
  • 无环
  • 根节点可以到达任意一个节点
  • 根节点入度为 0 ,其他节点入度为 1

然后是最小树形图:
边权和最小的树形图。

12.1朱刘算法

每个点选最小入边

之后如果有环,缩点,更改入环边权值

反复操作,直到无环

O(VE)

不定根

\(W=\sum w\)

建虚拟源点,对各个点连 \(W+1\) 边权的边

\(ans>=2W\) 则无解(两个根),否则减去 \(W+1\) 得到原值

\[QWQ \]

13.匹配

概念

1

设一般无向图 𝐺 = (𝑉 , 𝐸)。匹配 𝑀 ⊆ 𝐸 中任意两条边没有公共端点。

交错路:匹配边和非匹配边交错

增广路(匹配图):两端不匹配的交错路

增广路(Dinic):在“当前残量网络”中,从源点 S 到汇点 T 的一条路径,并且路径上的每一条边都必须严格满足“剩余容量 > 0” 且 “层级(Level)逐层 +1

两个定义本质相同,匹配里的增广路,是“去掉 S 和 T 后,将容量全设为 1 的残量网络增广路”。

沿增广路把“匹配/非匹配”取反,内部点仍恰好关联一条匹配边,两端由未匹配变为已匹配,因此 |𝑀| 增加 1。

2

一条关于 M 的增广路 P,必须同时满足以下 3 个条件:

条件1:这条路的起点和终点,在当前的匹配 M 中,都必须是尚未匹配的顶点

条件2:这条路上的边,必须是“未匹配边 -> 匹配边 -> 未匹配边 -> 匹配边……”这样交替出现的。

条件3:因为路径两端的边都是“未匹配边”,所以这条路上未匹配边的数量,永远比匹配边多 1 条,整条路径的边数一定是奇数。

引理

匹配的对称差:两个匹配的对称差等于他们的边集的异或

  • 其每个连通分量都是 𝑀, 𝑀′ 边交替的路或偶环。

  • 环与首尾分属 𝑀, 𝑀′ 的交错路中,两类边数相同;

  • 首尾均为 𝑀′ 边时 𝑀′ 边多一条,首尾均为 𝑀 边时反之。

Berge 引理:匹配 𝑀 最大,当且仅当不存在关于 𝑀 的增广路。

定理

柯尼希定理:二分图的最大匹配大小等于最小点覆盖大小。

算法

匈牙利(kuhn)算法

求解二分图最大匹配问题

暴力找增广路,翻转

优势在于写法简单 \(O(EV)\)

#include<cstring>
#include<iostream>
using namespace std;int n1,n2,m;
const int N=510,M=5e4+10;
int head[N],to[M],ne[M],vis[N],match[N],idx;
void add(int u,int v){
	to[++idx]=v;ne[idx]=head[u];head[u]=idx;
}
bool find(int u){
	for(int i=head[u];i;i=ne[i]){
		int j=to[i];
		if(!vis[j]){
			vis[j]=1;
			if(!match[j]||find(match[j])){
				match[j]=u;
				return 1;
			}
		}
	}
	return 0;
}
int main(){
	cin>>n1>>n2>>m;
	while(m--){
		int u,v;cin>>u>>v;
		add(u,v);
	}
	int res=0;
	for(int i=1;i<=n1;i++){
		memset(vis,0,sizeof vis);
		if(find(i)) res++;
	}
	cout<<res;
}

HK(Hopcroft-Karp)算法

分层图加速匈牙利

比Dinic常数小一点,都是 \(O(E\sqrt V\)),但是其写法和Dinic惊人相似,本质是一个算法

KM算法

求解二分图最大权完美匹配,不建议用网络流替代,会很慢

KM 算法:复杂度是 O(n³)。

替代品(最小费用最大流):在稠密图(带权匹配通常是完全二分图)中,复杂度约为 O(n³ log n) 甚至 O(n⁴)(取决于具体实现,如 SSP 增广路算法)。

//dfs版本,复杂的O(n^4)
#include<bits/stdc++.h>
#define int long long
#define inf 2e9
using namespace std;int n;
const int N=510;
int mp[N][N];
int lx[N],ly[N],vx[N],vy[N],march[N];
int d;
bool dfs(int u){
	vx[u]=1;
	for(int i=1;i<=n;i++){
		if(vy[i]) continue;
		if(lx[u]+ly[i]==mp[u][i]){
			vy[i]=1;
			if(!march[i]||dfs(march[i])){
				march[i]=u;
				return 1;
			}
		}
		else d=min(d,lx[u]+ly[i]-mp[u][i]);
	}
	return 0;
}
void KM(){
	for(int i=1;i<=n;i++){
		while(1){
			memset(vx,0,sizeof vx);
			memset(vy,0,sizeof vy);
			d=inf;
			if(dfs(i)) break;
			for(int j=1;j<=n;j++){
				if(vx[j]) lx[j]-=d;
				if(vy[j]) ly[j]+=d;
			}
		}
	}	
}
signed main(){
	memset(mp,-0x3f,sizeof mp);
	memset(lx,-0x3f,sizeof lx);
	int m;cin>>n>>m;
	while(m--){
		int a,b,c;cin>>a>>b>>c;
		mp[a][b]=max(mp[a][b],c);
		lx[a]=max(lx[a],c);
	}
	KM();
	int ans=0;
	for(int i=1;i<=n;i++){
		ans+=mp[march[i]][i];
	}
	cout<<ans<<"\n";
	for(int i=1;i<=n;i++) cout<<march[i]<<" ";
	return 0;
}
//BFS版
#include<bits/stdc++.h>
#define int long long
#define inf 2e9
using namespace std;int n;
const int N=510;
int mp[N][N];
int lx[N],ly[N],vx[N],vy[N],march[N],lm[N];
int slack[N];
int pre[N];
void aug(int v){
	int t;
	while(v){
		t=lm[pre[v]];
		lm[pre[v]]=v;
		march[v]=pre[v];
		v=t;//跳了两步,保证右部点
	}
}
void bfs(int s){
	memset(vx,0,sizeof vx);
	memset(vy,0,sizeof vy);
	memset(slack,0x3f,sizeof slack);
	
	queue<int> q;
	q.push(s);
	while(1){
		while(q.size()){
			int u=q.front();q.pop();
			vx[u]=1;
			for(int v=1;v<=n;v++){
				if(vy[v]) continue;//被询问过直接跳过
				if(lx[u]+ly[v]-mp[u][v]<slack[v]){//保证slack尽量小
					slack[v]=lx[u]+ly[v]-mp[u][v];
					pre[v]=u;//记录当前链
					if(slack[v]==0){
						vy[v]=1;
						if(!march[v]){
							aug(v);//翻转链
							return ;
						}
						else q.push(march[v]);//拓展相等子图
					}
				}
			}
		}
		int d=inf;
		for(int i=1;i<=n;i++){
			if(!vy[i]) d=min(d,slack[i]);//对非相等子图求最小边
		}
		for(int i=1;i<=n;i++){
			if(vx[i]) lx[i]-=d;
			if(vy[i]) ly[i]+=d;
			else slack[i]-=d;//从定义出发
		}
		//连带修改
		for(int i=1;i<=n;i++){
			if(vy[i]) continue;
			if(slack[i]==0){
				vy[i]=1;
				if(!march[i]){
					aug(i);
					return ;
				}
				else q.push(march[i]);
			}
		}
	}
}
void KM(){
	for(int i=1;i<=n;i++){
		bfs(i);
	}
}
signed main(){
	memset(mp,-0x3f,sizeof mp);
	memset(lx,-0x3f,sizeof lx);
	int m;cin>>n>>m;
	while(m--){
		int a,b,c;cin>>a>>b>>c;
		mp[a][b]=max(mp[a][b],c);
		lx[a]=max(lx[a],c);
	}
	KM();
	int ans=0;
	for(int i=1;i<=n;i++){
		ans+=mp[march[i]][i];
	}
	cout<<ans<<"\n";
	for(int i=1;i<=n;i++) cout<<march[i]<<" ";
	return 0;
}
//BFS的优化在于保留标记,修改边权之后接力迭代

13.偏序集最长反链(两两不可比较)

有限集合 𝑃 上的关系 ≼ 若满足自反、反对称、传递,则称 (𝑃, ≼)
为偏序集;记 𝑢 ≺ 𝑣 表示 𝑢 ≼ 𝑣 且 𝑢 ≠ 𝑣。
链中的元素两两可比较,反链中的元素两两不可比较。链划分要求
每个元素恰属于一条链。

Dilworth定理:有限偏序集的最长链大小等于最小链划分大小。

14.Hall定理

Hall定理:二分图 𝐺 = (𝑋, 𝑌 , 𝐸) 存在 𝑋 的完美匹配,当且仅当任意 𝑆 ⊆ 𝑋 都满足|𝑁(𝑆)| ≥ |𝑆|.

缺陷Hall定理:义缺陷 def(𝑆) = |𝑆| − |𝑁(𝑆)|.最大匹配覆盖的左部点数满足 \(|𝑀∗| = |𝑋| − max_{𝑆⊆𝑋}def(𝑆).\)

P14598:如果左部点若干集合独立,可以分别求解再相加

带权hall定理:

EX

射线法(计算几何)

posted @ 2026-08-25 09:25  Aistyr  阅读(7)  评论(0)    收藏  举报