来点同余最短路

简介

同余最短路,是一种用于解决一类特殊的完全背包类问题的算法。

这类问题形如:给定若干个正整数 \(a_i\),不限次使用,对它们能拼凑成的数进行一些计数。这些拼凑成的数通常很大。

同余最短路的核心思想,可以概括为:

\(m\) 为给定的正整数中的最小值,\(i<m\),则

\[\forall b=\sum a_ix_i,b=km+i \]

每个符合条件的 \(b\) 都能用唯一的 \((k,i)\) 表示。

对于每个 \(i\),找到最小的满足 \(\exists b=\sum a_ix_i,b=km+i\)\(k\),即可对 \(b\) 进行计数。

具体地,由每个 \(i\)\((i+a_j)\bmod m\) 连边,边权为 \(a_j\),以 \(0\) 为起点跑最短路,就能得到对于每个 \(i\) 满足条件的最小的 \(km+i=dis_i\)

实现上,可以使用堆优化 dijkstra。为节省空间,不需要把边显式地建出来。

例题

P2371 [国家集训队] 墨墨的等式

差分,变成求 \([1,n]\) 中符合条件的数的个数。

求出 \(dis_i\) 后对 \(x\in[dis_i,n],x\equiv i\pmod m\) 的解个数求和。

最终答案为:

\[\large\sum_{i=0}^{m-1}\lfloor\frac{r-dis_i}m\rfloor-\lfloor\frac{l-1-dis_i}m\rfloor \]

需要注意细节,特判 \(l,r\) 是否小于 \(dis_i\) 等,防止算多或者算少。

跑步锻炼

题意

给出 \(k\)\(d(1,2),d(2,3),d(3,4),d(4,1)\),每次可以从 \(i\) 跑到 \(i+1\)\(i-1\)\(4\) 可以到 \(1\)\(1\) 可以到 \(4\)),起点和终点只能是二号点,每个点可以经过任意次。要求在 \(4\) 个点之间一直跑,直到路程大于等于 \(k\)。求出满足条件的最小路程。

可以通过在 \((1,2)\)\((2,3)\) 上来回跑来刷路程,所以令 \(m=2\min(d(1,2),d(2,3))\)

最短路部分与前面的区别是需要加上所在点这一维。最后需要对每个 \(i\) 求出 \(\ge k\) 的最小的 \(km+i\),求它们的最小值。

最终答案为:

\[\large\min_{i=0}^{m-1} \begin{cases} dis_{i,2}&(dis_{i,2}\ge k)\\ dis_{i,2}+m\lceil\frac{k-dis_{i,2}}m\rceil&(dis_{i,2}<k) \end{cases} \]

P3403 跳楼机

为了方便计算,先把值域 \([1,n]\) 变为 \([0,n-1]\)

直接计算能到达的楼层是困难的,所以考虑不能到达的楼层。

由简介中的定义可知,对于每个 \(i\),满足 \(x\in[0,dis_i),x=km+i\) 的所有 \(x\) 都是不能到达的。而每个 \(i\) 求出的这样的 \(x\) 都不会有重叠,可以直接计数。

最终答案为:

\[\large n-\sum_{i=0}^{m-1} \begin{cases} \frac{dis_i-i}m&(0\rightarrow i)\\ \lfloor\frac{n+i-1}m\rfloor&(0\not\rightarrow i) \end{cases} \]

实现上,在处理永远无法到达 \(i\) 的情况时,为了防止溢出以及方便计算,可以令代码中 \(dis_i\) 初始值为 \(n+2i-1\)。这样计算答案时就会把 \(\lfloor\frac{n+i-1}m\rfloor\) 计入答案。

posted @ 2025-08-26 22:11  整齐的艾萨克  阅读(21)  评论(0)    收藏  举报