同余最短路

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

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

经典题目:跳楼机

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

Copy
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 求解。

绕圈法#

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

Copy
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 种物品单个体积为 vi、价值为 ci

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

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

1n50,1vi105,1ci106,1q105,1011V1012

题解

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

考虑模 m 下跑同余最短路时,对于两个余数相同的状态 (V1,C1)(V2,C2),假设后来有个询问 V 满足 Vmodm=V1modm(=V2modm),我们将会选择 ViVCi+VVimw 最大的状态。因此可以把 CiVimw 作为状态的权值来比较更新。(后面会证明这样得到的答案必定满足 ViV

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

因为无正环,所以最长路无环,所以最多 m 条边,即最多用了 m 个物品,因此最多体积 m×maxv,即 maxv21010<1011,所以这样得到的答案必定满足 ViV

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

使用字符串 S 的所有 border 长度能拼出来 w 以内的多少个数?|S|5×105,w1018

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

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

Small Multiple#

给定 n,求 n 的倍数中,数位和最小的那一个数的数位和。
1n105

Small Multiple 题解#

  • 每个十进制数都可以由“乘十”和“加一”来表示,其中“加一”的次数即为数位和。
  • 因为要求 n 的倍数,即模 n 为0,因此考虑在模 n 下的从 10 的最短路。
posted @   JiaZP  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
CONTENTS