1. (前置知识)Boruvka
  2. Kruskal 重构树
  3. 分层图选讲(包括斯坦纳树)
  4. 竞赛图 .5. 杂项:树哈希,弦图,支配树

Boruvka

对每个当前连通块,找它和别的连通块之间的边中,边权最小的。

然后把所有找到的边都加入最小生成树。不会成环。

复杂度 O(nlog⁡n)O(n\log n)。

Kruskal 重构树

每一次加边会合并两个集合,我们可以新建一个点,点权为加入边的边权。

同时将两个集合的根节点分别设为新建点的左儿子和右儿子,将新建点设为根。

在进行 n−1n-1 轮之后我们得到了一棵恰有 nn 个叶子的二叉树,同时每个非叶子节点恰好有两个儿子,这就是 Kruskal 重构树。

OIWIKI 的图:

mst5 mst6

是不是太特殊了,来个更特殊的。

图片描述 图片描述

性质:原图中两个点之间的所有简单路径上最大边权的最小值 = 最小生成树上两个点之间的简单路径上的最大值 = Kruskal 重构树上两点之间的 LCA 的权值。

P4768 [NOI2018] 归程

P4768 [NOI2018] 归程。

这道题问的是路径的最小和。

我们发现前一段的海拔大于当天的水位线,这个限制我们可以用 Kruskal 重构树 去解决一下。我们需要找到所有能到达的点,也就是重构树上的一个子树。

然后答案就是从可以到达的点集中距离 11 最近的。

利用性质:边的边权单调。

P13548 [OOI 2022] Air Reform

P13548 [OOI 2022] Air Reform。

思路

这道题给出了图 G,我们需要在 G 的反图上的最小生成树进行查询。

首先,我们先考虑对原图作最小生成树。我们发现 Kruskal 中,每次连接两个联通分量,然后连接它们的边权 ww 是单调不降的。

所以我们考虑在这个过程中计算反图的最小生成树。我们连接原图的边时,合并的连通块的点需要在反图上也合并一下。

那我们需要怎么合并?需要暴力启发式合并。

简单总结一下,对于瓶颈路问题,最小生成树可以替代 Kruskal 重构树,但是“只能走边权 ≤k\le k”这种问题必须要用。

CF1583H Omkar and Tours

CF1583H Omkar and Tours。

使一个节点和另一个节点 LCA 的 dfs 序最小,另一个节点选 dfn 最小或最大的节点。

思路

这道题直接利用重构树上的 LCA 就是路径权值最大值这个性质做就行了。

对于第二问,只需要求出 dfn 最小的和最大的叶子和这个节点的 LCA。

分层图

P4568 [JLOI2011] 飞行路线

P4568 [JLOI2011] 飞行路线。

每走一层就是用了一次免费搭乘飞机的机会。

P3831 [SHOI2012] 回家的路

P3831 [SHOI2012] 回家的路。

分层,横纵各是一层,然后层间就是换乘站。特殊的,起点和终点换乘不需要时间。

P9370 [APIO2023] 赛博乐园 / cyberland

P9370 [APIO2023] 赛博乐园 / cyberland。

差不多,就是用一次就走一层。

这道题比较独特的就是清零了就相当于可以从这个点重新开始。

代码不好写,vector 建图还被卡常了。

斯坦纳树

给定一个图,需要求一个最小子图,包含给定的点,并且联通。给定的点很少,需要状压。

P6192 【模板】最小斯坦纳树

P6192 【模板】最小斯坦纳树。

考虑状压 dp\text {dp},设 fi,Sf_{i,S} 表示以 ii 为根的答案中包含集合 SS 中所有点的最小边权值和。

f(i,S)←min⁡(f(i,S),f(i,T)+f(i,S−T))f(i,S)\leftarrow \min(f(i,S),f(i,T)+f(i,S-T))

f(i,S)←min⁡(f(i,S),f(j,S)+w(j,i))f(i,S)\leftarrow \min(f(i,S),f(j,S)+w(j,i))

我们的两个转移分别覆盖了加边和加点两种情况,所以完整。

时间复杂度 O(n×3k+nm×2k)O(n\times 3^k+nm\times 2^k)。

P4294 [WC2008] 游览计划

P4294 [WC2008] 游览计划。

这道题求的是点权,所以需要改一下。

思路

f(i,S)←min⁡(f(i,S),f(j,S)+ai)f(i,S)\leftarrow \min(f(i,S),f(j,S)+a_i)

然后 aia_i 多加了,需要减回去。

f(i,S)←min⁡(f(i,S),f(i,T)+f(i,S−T)−ai)f(i,S)\leftarrow \min(f(i,S),f(i,T)+f(i,S-T)-a_i)

确实,这题的难点并不在于如何求出答案,而在于打印方案。

打印方案就是最后 dfs 一遍,看看需要从哪里转移。

注意点的范围是 n∗mn*m 而不是 nn。

P3264 [JLOI2015] 管道连接

P3264 [JLOI2015] 管道连接。

每个情报站有一个特定的频道,需要 使得任意相同频道的情报站之间都建立通道连接。

这道题是求完斯坦纳树的 dp 之后再进行一次 dp。

思路

gig_i 表示“使得状态 ii 中所有颜色的要求都被满足”的最小花费。

中间需要用一个 wiw_i 表示连接 ii 这个子集的花费。

这个 dp 可以通过转移多个子集连一起的情况,可以计算公用连边。

树哈希

P5043 【模板】树同构 / [BJOI2015] 树的同构

P5043 【模板】树同构 / [BJOI2015] 树的同构。

一种好写的树哈希。

这道题找到重心,然后计算哈希就行了。

P4323 [JSOI2016] 独特的树叶

P4323 [JSOI2016] 独特的树叶。

刚才的哈希可能稍显复杂,看到题解中还有这种:

f(x)=∑y∈Sonxf(y)×P(Size(y))mod  Mf(x)=\sum\limits_{y\in Son_x}f(y)\times\text{P}(Size(y))\mod M

用...质数表。

思路

我们先用换根 dp 求出 AA 所有节点的哈希值,然后再求出 BB 的。

对于 BB 的每一个根,只需要枚举删去的根节点,就行了,删去根节点还是很好算的。

令 h(x)h(x) 为 xx 子树中去除了 yy 的哈希值

h(x)=(f(x)−f(y)×P(Size(y))×P(n−Size(y))mod  Mh(x)=(f(x)-f(y)\times\text{P}(Size(y))\times\text{P}(n-Size(y))\mod M

f′(y)=f(y)+(f(x)−f(y)×P(Size(y))×P(n−Size(y))mod  Mf'(y)=f(y)+(f(x)-f(y)\times\text{P}(Size(y))\times\text{P}(n-Size(y))\mod M

P8499 [NOI2022] 挑战 NPC Ⅱ

P8499 [NOI2022] 挑战 NPC Ⅱ。

思路

我们发现 nn 很大,kk 很小。然后这道题的根节点又是固定的。

所以我们先把没用的地方删掉,也就是已经相同的子树删掉。

所以最后我们最多剩下 55 个分支,暴力匹配复杂度差不多 nk!nk!。

实现的话我们计算 fx,yf_{x,y} 表示能否使 xx 和 yy 匹配。

弦图

弦图:没有长度 >3> 3 的纯环的图。

性质:区间图是弦图。看到最大独立集(团)、染色数等问题,可以猜这是一个弦图。

完美消除序列

定义

单纯点:邻集加上自己是团。

完美消除序列:viv_i 在 vi,vi+1,…,vnv_i , v_{i+1}, … , v_n 的导出子图内是单纯点。

是弦图 等价于 有完美消除序列。

线段图是弦图。

最大势算法

用来求完美消除序列的,O(n)O(n) 但是可以 O(nlog⁡n)O(n\log n) 写着更方便。

从 nn 到 11 给点标号(即倒序填完美消除序列),每次选择最多邻居是 已标号的点的点标号。

然后判断这个是不是就行了。

性质

Lemma 1:

团数 ω(G)≤χ(G)\omega(G)\le \chi(G) 色数

证明:考虑单独对最大团的导出子图(就是选取一些点,然后连边原图有的新图也要有)进行染色,至少需要 ω(G)\omega(G) 种颜色。

P14506 【模板】弦图

P14506 【模板】弦图。

这道题直接用最大势算法判断。

然后写法的话就是对于每一个点,看它的后继与剩余的相邻的点是否有连边就行了,可以理解为具有传递性吧,就是其它情况前面的节点已经判断过了。

  • 最大团大小:对每个点,它加上它后面邻居的个数,取最大值。因为每个点与后面邻居构成团。

  • 色数:因为色数大于等于最大团的大小,然后构造等于,一种构造方法:按完美消除序列从后往前贪心染色:每个点的后继邻居构成一个团,最多用 ω−1ω−1 种颜色,因此总能从 1∼ω1∼ω 中选到可用颜色,从而 χ≤ωχ≤ω。

  • 最大独立集:贪心:按完美消除序列从前到后,如果一个点没有被已选独立集中的点相邻,就选它,并标记其邻居不可选。简单理解一下,就是当前点和它后面连边的都在最大团里面,选了一定没有当前优。

代码
C++
#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int>e[200010],b[200010],l[200010];
int vis[200010],xl[200010],p[200010],h[200010],c[200010];

bool check(){
	for(int i=1;i<=n;i++){
		for(int v:e[i])if(p[i]<p[v])l[i].push_back(v);
	}
	for(int i=1;i<=n;i++){
		if(l[i].size()<2)continue;
		int u=l[i][0];
		for(int j=1;j<l[i].size();j++){
			int v=l[i][j];
			auto it=lower_bound(e[u].begin(),e[u].end(),v);
			if(*it!=v)return 0;
		}
	}
	return 1;
}
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)e[i].clear(),b[i].clear(),l[i].clear();
	for(int i=1;i<=n;i++)vis[i]=h[i]=c[i]=0;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	for(int i=1;i<=n;i++)sort(e[i].begin(),e[i].end());
	for(int i=1;i<=n;i++)b[0].push_back(i);
	int now=0;
	for(int i=n;i>=1;i--){
		int x=-1;
		while(1){
			while(!b[now].empty()){
				int v=b[now].back();
				b[now].pop_back();
				if(!vis[v]){
					x=v;
					break;
				}
			}
			if(x!=-1)break;
			now--;
		}
		vis[x]=1,xl[i]=x,p[x]=i;
		for(int v:e[x]){
			if(vis[v])continue;
			h[v]++;
			b[h[v]].push_back(v);
			if(h[v]>now)now=h[v];
		}
	}
	if(!check()){
		cout<<"No"<<endl;
		return;
	}
	cout<<"Yes"<<endl;
	for(int i=1;i<=n;i++)cout<<xl[i]<<' ';
	cout<<endl;
	int ans=0;
	for(int i=1;i<=n;i++){
		int res=1;
		for(int v:e[xl[i]])res+=(p[v]>p[xl[i]]);
		ans=max(ans,res);
	}
	cout<<ans<<' '<<ans<<' ';
	ans=0;
	for(int i=1;i<=n;i++){
		int u=xl[i],pd=1;
		for(int v:e[u]){
			if(c[v]){
				pd=0;
				break;
			}
		}
		if(pd){
			c[u]=1;
			ans++;
		}
	}
	cout<<ans<<endl;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	while(T--){
		solve();
	}
	
	return 0;
}

支配树

支配树:给定有向图 GG 和根 rr,支配树满足 xx 的子树恰好是那些删去 xx 后 rr 不可达的点。

DAG 上的支配树:P2597 [ZJOI2012] 灾难

P2597 [ZJOI2012] 灾难。

在 DAG 上求支配树然后求节点 size 即可。

例题:P7520 [省选联考 2021 A 卷] 支配

P7520 [省选联考 2021 A 卷] 支配。

思路

先暴力建出支配树,也就是枚举每一个点,看看删除它之后 11 能到达哪些点。对于每个点,在其支配点集中找到距离它最近的那个点作为父亲。

然后对于询问 x→yx\to y,我们发现这条边必须走。所以会影响到的点就是支配树上父亲不在 1→x1\to x 上的。

但是有点小问题,就是我们也需要标注 xx 到父亲路径上的点的直接子节点,因为不管走哪条路,都要经过父亲,所以支配节点不变。

有点卡常,入队立刻标记 visvis。

普通支配树:P5180 【模板】支配树

P5180 【模板】支配树。

Lengauer-Tarjan 算法

引入半支配点:一个点 kk 的半支配点是指一个 dfs 序最小的点 xx,使得 xx 可以通过一条路径 x, x1, x2, ⋯ , xcx,~x_1,~x_2,~\cdots,~x_c 到达点 kk,求满足对于任意 1≤i≤c1\le i\le c,有 dxi>dkd_{x_i}>d_k。

对于一个可以直接到达点 kk 的点 xx:

  • 若 dx≤dkd_x\le d_k 则 xx 可能是 kk 的半支配点。

  • 若 dx>dkd_x>d_k,x→kx\to k,则考虑其所有祖先 uu 满足 du>dkd_u>d_k,uu 的半支配点可能是点 kk 的半支配点。

按照 dfs 序考虑 kk。第一种很好算,第二种用值域并查集,慢慢合并,表示到根的路径上半支配点 DFS 序最小的节点。这里面的节点 dfs 序一定更大,因为是倒序。

然后需要利用半支配点求解支配点。

对于每个节点 xx,考虑从半支配点到它的路径(不含半支配点。dfs 树上的路径)上,半支配点的 DFS 序最小 的那个节点,记为 vv。

如果 vv 的半支配点等于 uu 的,vv 就是支配点。

如果 vv 的半支配点小于 uu 的,uu 的支配点就是 vv 的。就最后正序遍历的时候重新记录就行了。

竞赛图

竞赛图:nn 个点的有向图,任意两个点之间恰有一条有向边。

性质

缩点成链

竞赛图缩点后,所有 SCC 构成的东西呈链状,因此竞赛图上可以快速判断可达性。

链上的每个前后缀都是独立的“子竞赛图”。

关于哈密顿路径

存在一条哈密顿路径

考虑进行插点,插点进去。

对于原来的路径中,找出相对于新的点的出边,然后路径上下一个点是这个点的入边的位置插进去就行了。如果只有入/出就放开头/结尾。

竞赛图任意一个 SCC 存在一条哈密顿回路

同样是插点进去。

对于原来的路径中,先构造回路。

对于新的点,一定有入边和出边(不可能只有出边/入边连接这个点),那直接把这两个点之间的路径加上这个点就行了。

关于环的性质

  • 竞赛图若有环一定存在三元环。

  • 竞赛图的 k>=3k>=3 个点的 SCC 中一定存在 [3,k][3,k] 元环。

  • 有 (n3)−∑i=1n(outi2)\dbinom{n}{3}-\sum\limits_{i=1}^n \dbinom{out_{i}}{2} 个三元环(总-三个点中有一个点出度为 22 的情况)。

其它

  • 若 xx 的出度大于 yy 的出度,则 xx 可以到达 yy。

CF1498E Two Houses

CF1498E Two Houses。

若 xx 的出度大于 yy 的出度,则 xx 可以到达 yy。

证明+思路

这是因为竞赛图缩点后形成链状结构。同一个 SCC 显然。否则如果 xx 出度比 yy 大,xx 就在 yy 前面。

有了这个就可以直接做了。

注意 kk 是入度。

兰道定理

这个讲的比较详细。

我们定义比分序列为将每个点的出度 sis_{i} 从小到大排序的序列。

那么若满足 ∑i=1ksi≥(k2)\sum\limits_{i=1}^k s_{i}\ge \dbinom{k}{2} 且当 k=nk=n 时取等(即 ∑s=(n2)\sum\limits s=\dbinom{n}{2}),则一定能够造出一种竞赛图,反之不能。

必要性显然,考虑充分性证明,我们构造初始图,对于 j<ij<i 则 i→ji\to j 连边,设此时比分序列为 aa,这个序列显然在上述条件能够取到等号。保持 ∑i=1kai≤∑i=1ksi\sum\limits_{i=1}^k a_{i}\le \sum\limits_{i=1}^k s_{i},不断调整图直到 a=sa=s。

充分性怎么构造?

CF850D Tournament Construction

CF850D Tournament Construction。

思路

这道题 n≤60n \le 60,因为边数 ≤30×n\le 30\times n。

fi,j,kf_{i,j,k} 表示枚举到 ii 位的出度,ii 位出度为 aja_j,出度和为 kk,是否存在这样的方案。

如果 fn,m,n×(n−1)2=1f_{n,m,\frac{n\times(n-1)}{2}}=1,则合法,否则不合法。

然后考虑怎么构造。

对于 i>ji>j,ii 向 jj 连边,得到一个最初始的竞赛图。初始出度序列 ui=i−1u_i = i-1(升序),目标 dd 已升序。

只要 u≠du \neq d:

  • 找第一个位置 ii 使 di>uid_i > u_i(说明 ii 出度偏小)。然后找最后一个位置 jj 使 uj=uiu_j = u_i。
  • 找第一个位置 kk 使 dk<ukd_k < u_k(kk 出度偏大)。
  • 可以证明 j<kj<k 且 uj+2≤uku_j+2 \le u_k,因此存在一个顶点 xx 满足 k→xk\to x 且 x→jx\to j。(反证法,如果不存在,所有被 kk 指向的顶点都一定也被 jj 指向,但是 uu 不同)
  • 翻转这两条边:改为 x→kx\to k 和 j→xj\to x。结果 uku_k 减 11,uju_j 加 11,其余不变。

重复直到 u=du = d。

兰道定理求强连通分量

分界点:

每个点出度为 sis_i。

s_{j}=\binom{i}{2}]$$ ### P3561 [POI 2017] Turysta [P3561 [POI 2017] Turysta](https://www.luogu.com.cn/problem/P3561)。 [人才!](https://www.luogu.com.cn/article/0m2hb68e)。 其实这篇题解是利用竞赛图两两之间必有边的性质。