WIKIOI
wiki(I:)
比赛相关
工具软件
语言基础
算法基础
搜索
动态规划
字符串
数学
数据结构
图论
计算几何
杂项
专题
图论部分简介
图论相关概念
图的存储
DFS(图论)
BFS(图论)
树基础
树的直径
最近公共祖先
树的重心
树链剖分
树上启发式合并
虚树
树分治
动态树分治
AHU算法
树哈希
矩阵树定理
有向无环图
拓扑排序
最小生成树
斯坦纳树
最小树形图
最小直径生成树
最短路
拆点
差分约束
k 短路
同余最短路
强连通分量
双连通分量
割点和桥
2-SAT
欧拉图
哈密顿图
二分图
最小环
平面图
图的着色
网络流简介
最大流
最小割
费用流
上下界网络流
Prufer 序列
LGV 引理
弦图
46 objects
本站非官方,所收集资源均来源于网络。
同余最短路 - 图论
当出现形如“给定 $n$ 个整数,求这 $n$ 个整数能拼凑出多少的其他整数( $n$ 个整数可以重复取)”,以及“给定 $n$ 个整数,求这 $n$ 个整数不能拼凑出的最小(最大)的整数”的问题时可以使用同余最短路的方法。 同余最短路利用同余来构造一些状态,可以达到优化空间复杂度的目的。 类比 [差分约束](#) 方法,利用同余构造的这些状态可以看作单源最短路中的点。同余最短路的状态转移通常是这样的 $f(i+y) = f(i) + y$ ,类似单源最短路中 $f(v) = f(u) +edge(u,v)$ 。 ## 例题 ### "[P3403 跳楼机](https://www.luogu.com.cn/problem/P3403)" > 题目大意:给定 $x,y,z,h$ ,对于 $k \in [1,h]$ ,有多少个 $k$ 能够满足 $ax+by+cz=k$ 。 不妨假设 $x < y < z$ 。 令 $d_i$ 为只通过 **操作 2** 和 **操作 3** 能够达到的最低楼层 $p$ ,并且满足 $p\bmod x=i$ 。 可以得到两个状态: - $i \xrightarrow{y} (i+y) \bmod x$ - $i \xrightarrow{z} (i+z) \bmod x$ 注意通常选取一组 $a_i$ 中最小的那个数对它取模,也就是此处的 $x$ ,这样可以尽量减小空间复杂度(剩余系最小)。 那么实际上相当于执行了最短路中的建边操作: `add(i, (i+y) % x, y)` `add(i, (i+z) % x, z)` 接下来只需要求出 $d_0, d_1, d_2, \dots, d_{x-1}$ ,只需要跑一次最短路就可求出相应的 $d_i$ 。答案即为: $$ \sum_{i=0}^{x-1}\frac{h-d_i}{x} + 1 $$ 加一是由于当前所在楼层也算一次。 ### "参考实现" ```cpp #include
using namespace std; typedef long long ll; const int maxn = 100010; const int INF = 0x3f3f3f3f; ll h, x, y, z; ll head[maxn << 1], tot; ll dis[maxn], vis[maxn]; queue
q; struct edge { ll to, next, w; } e[maxn << 1]; void add(ll u, ll v, ll w) { e[++tot] = edge{v, head[u], w}; head[u] = tot; } void spfa() { dis[1] = 1; vis[1] = 1; q.push(1); while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for (int i = head[u]; i; i = e[i].next) { int v = e[i].to, w = e[i].w; if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if (!vis[v]) { q.push(v); vis[v] = 1; } } } } } int main() { memset(dis, INF, sizeof(dis)); scanf("%lld", &h); scanf("%lld %lld %lld", &x, &y, &z); if (x == 1 || y == 1 || z == 1) { printf("%d\n", h); return 0; } for (int i = 0; i < x; i++) { add(i, (i + z) % x, z); add(i, (i + y) % x, y); } spfa(); ll ans = 0; for (int i = 0; i < x; i++) { if (h >= dis[i]) ans += (h - dis[i]) / x + 1; } printf("%lld\n", ans); return 0; } ``` ## 习题 [洛谷 P3403 跳楼机](https://www.luogu.com.cn/problem/P3403) [洛谷 P2662 牛场围栏](https://www.luogu.com.cn/problem/P2662) [\[国家集训队\]墨墨的等式](https://www.luogu.com.cn/problem/P2371) [「NOIP2018」货币系统](https://loj.ac/problem/2951)