简介
同余最短路,是一种用于解决一类特殊的完全背包类问题的算法。
这类问题形如:给定若干个正整数 \(a_i\),不限次使用,对它们能拼凑成的数进行一些计数。这些拼凑成的数通常很大。
同余最短路的核心思想,可以概括为:
令 \(m\) 为给定的正整数中的最小值,\(i<m\),则
每个符合条件的 \(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\) 的解个数求和。
最终答案为:
需要注意细节,特判 \(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\),求它们的最小值。
最终答案为:
P3403 跳楼机
为了方便计算,先把值域 \([1,n]\) 变为 \([0,n-1]\)。
直接计算能到达的楼层是困难的,所以考虑不能到达的楼层。
由简介中的定义可知,对于每个 \(i\),满足 \(x\in[0,dis_i),x=km+i\) 的所有 \(x\) 都是不能到达的。而每个 \(i\) 求出的这样的 \(x\) 都不会有重叠,可以直接计数。
最终答案为:
实现上,在处理永远无法到达 \(i\) 的情况时,为了防止溢出以及方便计算,可以令代码中 \(dis_i\) 初始值为 \(n+2i-1\)。这样计算答案时就会把 \(\lfloor\frac{n+i-1}m\rfloor\) 计入答案。