图的遍历
图的遍历,就是在图上 D F S / B F S DFS/BFS DFS/BFS。是对图最基本的操作。
图的存储方式
-
邻接矩阵: O ( n 2 ) O(n^2) O(n2)
-
邻接链表: O ( m ) O(m) O(m)
int e=0,fir[N],to[M],nxt[M],w[M];
inline void adde(int x,int y,int z){
to[++e]=y,nxt[e]=fir[x],fir[x]=e,w[e]=z;
}
for(int i=fir[u];i;i=nxt[i])
- 邻接动态数组: O ( m ) O(m) O(m)
typedef pair<int,int> pii;
vector<pii> e[N];
inline void adde(int x,int y,int z){
e[x].push_back(pii(y,z));
}
for(int i=0;i<e[u].size();++i)
Part 1:树的遍历
树的遍历,就是统计树上的一些信息。例如
-
子树大小(也可以归为树形DP)
-
有根树上每个节点的父亲节点
-
某一点到其他点的最短路(特别注意:树上最短路是唯一的,因此无需使用最短路算法)
-
直径
-
重心
例题1:[NOI2011][luogu2052][FAIOJ13113]道路修建
对于每条边 ( u , v ) (u,v) (u,v),它的费用就是 w × ∣ n − 2 × s i z e v ∣ w×|n-2×size_v| w×∣n−2×sizev∣。
统计 s i z e size size即可。复杂度 O ( n ) O(n) O(n)
例题2:[NOIP2014][luogu1351][FAIOJ10141]联合权值
两点间最短路为 2 2 2,那么它们中间只有一个点。我们枚举这个点。
对于与这个点相邻的所有点,它们都会贡献一份乘积。
简单推导可知这些乘积之和为 ( ∑ W v ) 2 − ∑ W v 2 2 \dfrac {(\sum W_v)^2-\sum W_v^2}2 2(∑Wv)2−∑Wv2,最大值为 max W v × max 2 W v \max{W_v}×\max_2{W_v} maxWv×max2Wv。
对每个点都统计其相邻点的和、平方和、最大值、次大值即可。复杂度 O ( n ) O(n) O(n)。
例题3:[NOIP2007][luogu1099][FAIOJ1274]树网的核
题意简化:给定一棵树,求其直径上一条长度不超过 s s s的路径,使得树上其他顶点到该路径的距离最大值最小。
因为要求最大值最小,所以我们首先二分答案 x x x。
问题转化为求直径上一条不超过 s s s的路径使得树上其他顶点到该路径的距离都不超过 x x x。
根据直径的性质,其他顶点中距已知顶点最远的点一定是直径端点。
预处理直径上每个店距离一个端点的距离,从两个端点向中间缩即可。
复杂度为 O ( n log W ) O(n\log W) O(nlogW)。
注意:二分下界是最长支链的长度!
另外,本题还有单调队列的 O ( n ) O(n) O(n)做法。
Part 2:图的遍历
-
无向图的连通性
-
有向图中一个点与其他点的可达性
-
基环树上找环
例题4:[NOIP2017][luogu3958][FAIOJ10173]奶酪
如果将所有的洞和上、下边界都视为点,相交或相切视为连边,那么本题实际上就是在求上下边界是否连通。
从下边界开始遍历整张图,给遍历到的点都标记上(以免重复遍历),判断是否遍历到上边界即可。
最后就是基本的几何问题了。虽然是三维空间,但类别二维空间即可:
如何判断两个洞是否相交或相切?
R 1 + R 2 ≤ ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 + ( z 1 − z 2 ) 2 R_1+R_2\leq \sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2} R1+R2≤(x1−x2)2+(y1−y2)2+(z1−z2)2
如何判断洞和下边界是否相交或相切?
R ≤ z R\leq z R≤z
如何判断洞和上边界是否相交或相切?
R ≤ h − z R\leq h-z R≤h−z
由 B F S BFS BFS的过程可知,本题复杂度为 O ( n 2 ) O(n^2) O(n2)。
例题5:[NOIP2014][luogu2296][FAIOJ10144]寻找道路
如果不看第一个条件,那么本题就是简单的最短路问题。而由于边权均为1
,所以可用 B F S BFS BFS代替 S P F A SPFA SPFA求最短路,复杂度严格 O ( m ) O(m) O(m)。
我们发现第一个条件其实就是限制了有一些点是不能走的。因此我们找到所有不能走的点(即出边有无法到达终点的点)即可。
考虑什么情况下一个点可以到达终点。
一定是这个点通过一条指向终点的路径最终到达终点。但如果由每个点都 B F S BFS BFS一遍,复杂度将会达到 O ( n 2 ) O(n^2) O(n2),无法接受。
但如果我们建出反图,即将图中的边全部反向,那么问题就转化为由终点开始 B F S BFS BFS,若可以到达一个点,那么这个点在原图中就可以到达终点。
然后再由所有无法达到终点的点在反图上遍历其邻接点,将这些点都标记上不能走。
最后在原图中做 B F S BFS BFS求最短路即可。
例题6:[NOIP2015][luogu2661][FAIOJ10151]信息传递
本题包装比较复杂,但结论很简单:答案就是基环树森林的最小环长。
首先建图,由 i i i向 T i T_i Ti连边,则这是一张 n n n点 n n n边的图。由于每个点仅有一条出边,所以这张图其实是一片基环树森林。
如果一个环上的点传递出自己的信息,那么在每一轮中,这条信息都会在环上走一步。走完环长步后,这条信息就回到了原点。
因此当游戏进行完最小环长轮时,那个环上的所有点都收到了自己传出的信息。游戏结束。
D F S DFS DFS找环即可。复杂度 O ( n ) O(n) O(n)。
例题7:[NOIP2018][luogu5022][FAIOJ10183]旅行
题意:求图的最小 D F S DFS DFS序。
一般图的最小 D F S DFS DFS序只有指数级算法(暴搜)。
但本题有 m = n − 1 m=n-1 m=n−1或 m = n m=n m=n。
如果 m = n − 1 m=n-1 m=n−1,那么这张图就是一棵树。
直接贪心地从 1 1 1号点开始,并对每个点的子树以根节点编号从小到大的顺序开始遍历即可。
易知该贪心无后效性。
如果 m = n m=n m=n,那么这张图就是一棵基环树。
首先说明一点:之前的贪心是错的。
样例2就是一个反例。如果按原来的贪心,我们会得到这样的遍历顺序:
1 3 2 5 4 6
而最优策略显然是
1 3 2 4 5 6
因为这个贪心相当于断掉环上距环的起点最近的两条边中出点较大的一条,再去做上面树的情况的贪心。
显然,断掉环上其他边也有可能成为最优策略。
所以我们先用一次遍历找到环,然后枚举环上的断边做树的贪心,比较断每条边得到的字典序即可。
复杂度 O ( n 2 ) O(n^2) O(n2)。
例题8:[SDOI2012][luogu2498]拯救小云公主
这道题。。。你的路径可以是直的,也可以是圆弧的,甚至可以是各种奇形怪状的形状,所以模拟路径肯定是行不通的。
但题目也没要求我们模求出具体路径(当然硬要求一个也不是不行),因此我们并不关心路径具体是什么。
考虑二分答案。问题转化为你需要从 ( 1 , 1 ) (1,1) (1,1)走到 ( R , L ) (R,L) (R,L)且距离任意关键点的距离不能超过 x x x。
我们以关键点为圆心, x x x为半径画圆,则这些圆的范围都是“禁区”,即不能走的范围。
这样本题就转化为一个连通性问题。但是仍然做不了:一堆圆形禁区把原图划分得“千疮百孔”,完全无法构建图论模型。
我们反向考虑,把这些禁区以及上左、下右边界视为点。
如果两个边界连通,那么 ( 1 , 1 ) (1,1) (1,1)和 ( R , L ) (R,L) (R,L)就被这些禁区分开了。
因此由边界 B F S BFS BFS即可。判断连通条件仍然是相交或相切。
复杂度 O ( n 2 log R ) O(n^2\log R) O(n2logR)