同余最短路

  • 题型大概为若干个无限多个正整数来拼凑出某些数(类似完全背包)。
  • 套路为随便选一个数 \(M\) 转化为求 \(f(x)\) 表示拼凑出 \(x + kM\) 的最小 \(k\)。转化为最短路。
  • 该模型的强大之处在于将完全背包的结构清晰地展现出来。

同余最短路求能凑成多少(H以内的)数

经典题目:跳楼机

随便挑一个数 \(M\) 作为模数,求出上述 \(f(x)\) 即可算出答案。

inline void dij() {
	...
	while (!q.empty()) {
		...
		
		to = (cur + y) % x;
		if (vis[to])	continue;
		if (dis[cur] + y < dis[to]) {
			dis[to] = dis[cur] + y;
			q.push((node){dis[to], to});
		}
		...
	}
}

同余最短路求最大不能拼凑出来的数

“小凯的疑惑”加强版

经典题目:牛场围栏

特判掉存在 \(1\) 的情况和 gcd 不为 1 的情况。

随便找一个数(最小的数)当作模数,求出 \(f\) 后根据 \(f\) 求解。

绕圈法

我退役后才注意到的新做法。将同余最短路看作完全背包问题,最多加 \(\frac{m}{gcd(m,v)}\)\(v\),否则可以被 \(m\) 替换掉。因此可以对 \(Z_m\) 上由加 \(v\) 得到的 \(gcd(m, v)\) 个轨道分别顺序递推(例如对每个轨道找到最小值,从这里开始往后更新;或者直接推两遍)。复杂度是优秀的 \(O(nm)\) 且极其好写。

memset(f, 0x3f, sizeof(f));
f[0] = 0;
for (int i = 2; i <= n; ++i) {
     int v = a[i], g = get_gcd(a[1], a[i]);
     for (int j = 0; j < g; ++j) {
          int p = j;
          do {
               MIN(f[(p+v)%a[1]], f[p]+v);
               p = (p+v)%a[1];
          } while (p != j);
          do {
               MIN(f[(p+v)%a[1]], f[p]+v);
               p = (p+v)%a[1];
          } while (p != j);
     }
}

一类完全背包问题

题意

本题中,你需要解决完全背包问题。

\(n\) 种物品,第 \(i\) 种物品单个体积为 \(v_i\)、价值为 \(c_i\)

\(q\) 次询问,每次给出背包的容积 \(V\),你需要选择若干个物品,每种物品可以选择任意多个(也可以不选),在选出物品的体积的和恰好\(V\) 的前提下最大化选出物品的价值的和。你需要给出这个最大的价值和,或报告不存在体积和恰好为 \(V\) 的方案。

为了体现你解决 NP-Hard 问题的能力,\(V\) 会远大于 \(v_i\),详见数据范围部分。

\(1 \le n \le 50, 1 \le v_i \le 10^5, 1 \le c_i \le 10^6, 1 \le q \le 10^5, 10^{11} \le V \le 10^{12}\)

题解

直观想法是尽可能多去选性价比最高的物品(设其为 \((m, w)\)),问题是怎么样尽早达成余数要求,并且这里未必“越早越好”,还需要考虑达成余数要求时所产生的那部分额外收益。

考虑模 \(m\) 下跑同余最短路时,对于两个余数相同的状态 \((V_1, C_1)\)\((V_2, C_2)\),假设后来有个询问 \(V\) 满足 \(V \mod m = V_1 \mod m(= V_2 \mod m)\),我们将会选择 \(V_i \le V\)\(C_i + \frac{V-V_i}{m}w\) 最大的状态。因此可以把 \(C_i-\lfloor \frac{V_i}{m}\rfloor w\) 作为状态的权值来比较更新。(后面会证明这样得到的答案必定满足 \(V_i \le V\)

基于上述思想,同余最短(长)路转移时可以通过加入 \((v_i,c_i)\) 物品由 \((V^p, C^p)\) 转移到 \((C^q,V^q), q = (p+v_i)\mod m\),权值更新: f[q]=max(f[q], f[p]+c[i]-(p+v)/m*w),即跑最长路。因为 \((m,w)\) 性价比最高,所以图无正环。

因为无正环,所以最长路无环,所以最多 \(m\) 条边,即最多用了 \(m\) 个物品,因此最多体积 \(m \times maxv\),即 \(maxv^2 \le 10^{10} < 10^{11}\),所以这样得到的答案必定满足 \(V_i \le V\)

单调队列优化等差数列同余最短路

使用字符串 \(S\) 的所有 border 长度能拼出来 \(w\) 以内的多少个数?\(|S| \le 5 \times 10^5, w \le 10^{18}\)

border 长度集合可以划分为不超过 \(\log_2 |S|\) 个等差数列。对于等差数列 \(x, x+d, x+2d, \dots, x+ld\),在 \(\mod x\) 下跑同余最短路,则转移的时候是在每个轨道上用长度为 \(l\) 滑动窗口取最小值来转移。如何从模 \(p\) 切换到模 \(q\)?注意到我们处理出来的模 \(p\) 的结果告诉我们长度集合 \(\{dis_i + kp | 0 \le i \lt p, k \ge 0\}\) 是可以达到的,因此在模 \(q\) 下我们可以先把 \(dis_i\) 都加上去,然后再用 \(p\) 转移一遍。

当然还有一些奇奇怪怪的同余最短路题

Small Multiple

给定 \(n\),求 \(n\) 的倍数中,数位和最小的那一个数的数位和。
\(1\le n\le 10^5\)

Small Multiple 题解

  • 每个十进制数都可以由“乘十”和“加一”来表示,其中“加一”的次数即为数位和。
  • 因为要求 \(n\) 的倍数,即模 \(n\) 为0,因此考虑在模 \(n\) 下的从 \(1\)\(0\) 的最短路。
posted @ 2024-08-08 09:08  JiaZP  阅读(9)  评论(0编辑  收藏  举报