最短路专项

link

A -BZOJ3706

显然可以发现有解当且仅当仅保留所有黑色边时,每个连通块存在欧拉回路

最小操作次数可以考虑将黑色连通块缩成一个点,然后在原图里一个连通块拿出任意一颗生成树都可以将这里面的黑点全部消掉(走到黑点的时候走欧拉回路,树边都只会经过两次且都是白边)。

显然不存在比这个更小的解,所以答案就是包含黑色边的连通块个数。

这个维护是简单的,可以通过并查集做到 \(O(n\alpha(n))\),但是你可以使用DFS直接弄到 \(O(n)\)

B-「联合省选 2020 B」丁香之路

原题相当于是找一条欧拉通路包含所给边且起点终点分别是 \(s,i\)

注意到 \(n\le 2500\),所以考虑平方算法。

通路比较麻烦,可以先连上 \((s,i)\) 变成回路。那么显然对给定特殊边以及这条边先合并连通块,接下来对于奇度点显然有偶数个,且需要想办法合并,那么显然是数值上相邻的奇度点两两合并。

这时候问题又来了,现在变成了若干个存在欧拉回路的子图,每个子图大小不小于 \(2\),我们需要将其连接成一个欧拉回路。

将这些连通块看成一个点,变成欧拉回路的话,最优方案显然是原图中它们的最小生成树边权和的两倍(因为如果有 \(u\to v\to z\to u\),则根据最小生成树的原则,我们会选取其中较小的两条边,那么根据原题绝对值边权显然可以替代掉另一条边),而可能构成最小生成树的边只可能是数值上相邻但不在同一个连通块内的点。

那就完了。

C-「AHOI2014」骑士游戏

显然建图

发现 \(R\ge 1\)

处理一个dp :\(w_u=\min(K_u,S_u+\sum _{u\to v}w_v)\)

这很经典,由于所有值非负,所以将 \(K_u\) 扔进小根堆,更新其他点的 \(w\),当更新好了之后又将右边的值加入小根堆更新。某个点第一次被取出的时候就解决了它的 \(w\),因为小根堆取出的值总是不降的。这时候如果物抗还没有算出说明物抗不可能低于法抗

D-「BZOJ4070 Apio2015」雅加达的摩天楼

由于是由一只狗传递消息,所以不会出现两只狗互相跳来相遇的情况,也即任意时刻只会有一直狗在动。

一个比较暴力的转移是设 \((pos,v)\) 为当前知道消息的狗在 \(pos\) 处能力为 \(v\)

则当 \(v\le \sqrt n\) 时有 \(O(n\sqrt n)\) 的状态,而对于 \(v\ge \sqrt n\)\(pos\) 只有 \(\sqrt n\) 种,所以状态总数是 \(O((n+m)\sqrt n)\) 的。

注意到边权全是 \(1\),直接广搜即可。

阈值分治分析复杂度

E-「JOI 2020 Final」奥运公交

如果修改的边不在正反图的最短路径树上,则不会影响最短路,可以直接预处理四个状态的最短路直接拼起来。

否则重新跑一边算,这样的边仅有 \(O(n)\) 条。

F-「NOIP2017」逛公园

注意到 \(K\) 很小可以直接考虑DP。

如果有零环且可能被经过则无穷。

先处理最短路,那么走边 \((u,v,w)\) 相对于走最短路额外走了 \(w-(d_v-d_u)\) 的路。

所以设 \(f_{u,k}\)\(1\to u\),当前长度为 \(d_u+k\) 的方案数,直接转移就好了。

转移很简单的,\(d_v+k'=d_u+k+w\implies k'=d_u-d_v+k+w\)

G-「GXOI / GZOI2019」旅行者

直接跑多源最短路就好了,注意到可能自己回到自己,所以记录一个起点不同的次短路即可。

正解是二进制分组,这挺有启发,枚举每个 bit 位,将其 \(0/1\) 分组,可以在额外增加 \(O(\log V)\) 倍的时间复杂度内统计一整个集合两两之间的答案,要求可以快速计算两个集合之间的答案

还有一个想法是正反图跑dijkstra并染色,所要求的最短路必然不会经过第三个特殊点,且两个特殊点之间的路径存在一个中边,枚举它,左右端点的最优解不同,直接算进答案,显然不会少算,也不会算到更优的非法解。

H-「COCI2017-2018#3」portal

直接暴力,要么按部就班地走,要么就可以花费到四个方向墙最短的距离走到四个方向任意一个墙的旁边,枚举转移即可。

I-「CEOI2014」The Wall

垃圾东西啊,这东西好像在哪里见过

首先可以发现所有点到左上角的最短路都会被最优方案围着,如果不是则用最短路替换掉显然不劣。

问题变成找一条回路包含所有的最短路径。

我们把路径画出来,将两个格子的十字交界拆为 4 个点,抄下图

	(1)---(2)
	 | (0) | 
	(3)---(4)
	
       1|2   (x-1,y)   1|2
---------------------------------
       3|4             3|4
(x,y-1) |     (x,y)     |   (x,y+1)
       1|2             1|2
---------------------------------
       3|4   (x+1,y)   3|4

然后一条边就被拆为了两条路,方便回路行走。

对于这样的虚点连边,不能够越过原先的最短路径,也不能够使用在村庄范围内的虚点,连边可以由这个图解释,注意左上角连边需要特判。

	(1)-0-(2)---w---(1)-0-(2)
	 |     |         |     0 
	(3)-0-(4)---w---(3)-0-(4)
	 |     |         |     |
	 w     w         w     w
	 |     |         |     |
	(1)-0-(2)---w---(1)-0-(2)
	 0     0         0     0 
	(3)-0-(4)---w---(3)-0-(4)

跑这样的一个最短路就行了,它不会穿过任意最短路,也会包含所有村庄。

写起来是真的臭

J-「JOISC 2020 Day4」治疗计划

时间轴很麻烦啊,能不能统一?感觉相当抽象

考虑一个基本的DP,设 \(f_{t,i}\) 为时刻 \(t\)\([1,i]\) 的人已经治好了的最小花费。

那么我们可以枚举 \(T_i\le t\) 的任意决策进行顺推,它决策之后可能会扩展这个区间。

这个显然是对的,怎么优化啊

\(t\) 大概是不必要的,我们只需要记录当前的区间右端点是由谁贡献的即可。

把状态当作点,是个最短路,起点应当是 \(l=1\) 的,终点是 \(r=n\) 的。

按照 \(t\) 排序,也即 \(i<j\implies t_i\le t_j\),能够接续贡献的只有两个边:

\[\begin{cases} l_j+(t_j-t_i)\le r_i+1\implies l_j+t_j\le r_i+t_i+1 :i\to j\\ l_i+(t_j-t_i)\le r_j+1\implies l_i-t_i\le r_j-t_j+1 :j\to i\\ \end{cases} \]

如何优化建图?

注意这些偏序都很简单,直接CDQ排序之后做前缀优化建图即可

K-「HNOI2009」最小圈

二分答案判负环即可。

L-「SCOI2011」糖果

比较蠢的差分约束题。

首先差分约束建图,然后 tarjan 缩点,直接拓扑排序即可。

如果一个SCC里面的边边权非零则非法。

M-CF1521D

树的最小剖分。

其实就是要断掉最少的边使得原树变为若干链。

那么可以转化为尽可能保留更多的边

那么一个点最多保留两条,且由于每条边保留贡献相同,能保留就保留。

所以直接贪心 dfs,如果当前可以合并两条链直接合,多出的链就不管了,否则如果有一条链就传给父亲解决。

这显然是对的。

N-arc121E

给定一颗有根树,要求有多少个排列 \(p\)

满足 \(p_i\) 不是 \(i\) 的祖先

你要求 \((i,p_i)\),可以等效为求 \((p_i,i)\),也就是说保证 \(p_i\) 不是 \(i\) 的后代这样的排列也是可以的。

问题得到转化,考虑容斥。

\(f_{u,k}\) 为子树 \(u\) 钦定了 \(k\) 个位置非法的方案数,只考虑这 \(k\) 个位置的分配情况, \(F(i)\) 是钦定 \(i\) 个位置非法的方案数,显然是 \(f_{1,i}(n-i)!\),答案显然是 \(\sum (-1)^iF(i)\)

\(f_{u,k}\) 首先是做一个树上背包转移,然后可以枚举取自己由 \(f_{u,k-1}\) 转移。取自己的方案数就是 \(siz_u-k\)

O-arc121F

容易发现 and 0 和 or 1 的话另一半什么情况是没有意义的

进一步感觉先 and 会比先 or 优秀一点

发现把 and 先弄完再弄 or 是最优的

然后不合法当且仅当每个 and 连通块都死了,容斥这一步,钦定每一个 and 连通块都存在零即可,那么只需要设 \(f_{i,0/1}\) 为解决完子树 \(i\) 后当前 \(i\) 所在连通块有没有出现过 \(0\) 即可。转移的时候枚举这条边是 and 还是 or 就行了。

P-「COCI 2020.11」Svjetlo

先删掉全亮的子树。

显然每棵树最多会进去一次,最多会出来一次,那么路径端点就只能是有 \(0/1/2\) 个出现在子树内。

而外部怎么走与树内部怎么走无关,你走出去了再走回来就可以省掉走出去的部分看成子树根节点走两次。

所以可以设 \(f[u,0/1,0/1/2]\) 表示子树 \(u\) 除了 \(u\) 全亮,\(u\) 的状态是 \(0/1\),而路径端点有 \(0/1/2\) 个在子树内。

显然第三维是个背包的转移。前面的转移就相当恶心的分类讨论。

一个核心是往返一次会改变两个节点的状态。

对于 \(0+0\) 的合并,根据最后儿子和根是走一次还是往返一次来判断。

对于 \(0+1\) 的合并,先根据最后是走一次还是往返判断,然后再根据起点在哪边讨论,一共四种情况

对于 \(0+2,1+1\) 本质不变。

最后你可以选择当前点作为路径的一个端点来更新(注意可以是回路)。

Q-CF1442E

如果只有黑白点,那么显然是相连的同色点缩成一个点之后的直径长度

也可以看作同色点之间距离是0

然后加上灰色无非就是两色选一,设 \(f_{i,0/1}\) 为当前点是这个颜色时的最长链的最小,\(g_{i,0/1}\) 表示当前状态下的直径最小长度。

答案显然是 \(\lceil\frac{\max(\min g_{i,0},g_{i,1})}{2}\rceil\)

R-CF512D

一个环显然无法操作,而对于一棵树而言就是一个拓扑序。

那么一个点如果属于某个环,这就是不动点。

如果一个连通块都是树,那么没有限制,只要满足某种拓扑序

否则把有环的连通块中可动点拿出来会组成一个森林,且有固定的根最后才能够删去(不可能有两个根,否则两个根路径上的点都是环上点)。

先考虑固定根的情况,直接就是拓扑序计数,跑一个背包即可。

对于一般树的情况,需要想办法去重。

不妨我们钦定根,且根不会被删除且是没有被删除的点里编号最小的,则编号小于根的所有子树是删完了的。

略微更改一下DP数组即可(即遇到钦定删完的就只保留删完的状态其余全部清空)。

最后各个部分的答案就直接卷积即可。

posted @ 2024-10-05 16:46  spdarkle  阅读(6)  评论(0编辑  收藏  举报