P3403 跳楼机
题目背景
DJL 为了避免成为一只咸鱼,来找 srwudi 学习压代码的技巧。
题目描述
Srwudi 的家是一幢 h h h 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。
经过改造,srwudi 的跳楼机可以采用以下四种方式移动:
- 向上移动 x x x 层;
- 向上移动 y y y 层;
- 向上移动 z z z 层;
- 回到第一层。
一个月黑风高的大中午,DJL 来到了 srwudi 的家,现在他在 srwudi 家的第一层,碰巧跳楼机也在第一层。DJL 想知道,他可以乘坐跳楼机前往的楼层数。
输入格式
第一行一个整数 h h h,表示摩天大楼的层数。
第二行三个正整数,分别表示题目中的 x , y , z x, y, z x,y,z。
输出格式
一行一个整数,表示 DJL 可以到达的楼层数。
输入输出样例 #1
输入 #1
15
4 7 9
输出 #1
9
输入输出样例 #2
输入 #2
33333333333
99005 99002 100000
输出 #2
33302114671
说明/提示
可以到达的楼层有: 1 , 5 , 8 , 9 , 10 , 12 , 13 , 14 , 15 1,5,8,9,10,12,13,14,15 1,5,8,9,10,12,13,14,15。
1 ≤ h ≤ 2 63 − 1 1 \le h \le 2^{63}-1 1≤h≤263−1, 1 ≤ x , y , z ≤ 1 0 5 1 \le x,y,z \le 10^5 1≤x,y,z≤105。
同余最短路:
如果常规方法建图的话会发现图不仅复杂而且占用空间大,所以就需要用同余最短路来解决。
该算法的核心思想是:对于每个余数 r,找到能到达的最小楼层 k,使得 k ≡ r (mod x),然后统计所有不超过 h 的、与 k 同余的楼层数目。
说人话就是,题目给出了大楼的最大楼层数 h h h,然后还给出了几个数(比如跳楼机每次能向上移动的层数 x 、 y 、 z x、y、z x、y、z。我们把所有楼层按照除以 x x x(其中一个数,这里是 x x x) 的余数来分类,余数会有 0 0 0 到 x − 1 x - 1 x−1这 x x x 种情况。
对于每一种余数 r r r(也就是每一类楼层),我们要通过跳楼机的移动方式,找到能到达的最小的那个楼层 k k k,这个楼层 k k k 除以 x x x 余数正好是 r r r。
找到这个最小楼层 k k k 之后,我们就可以知道所有和(k)除以(x)余数相同的楼层了,它们之间的差值肯定是 x x x 的整数倍。然后我们就数一下,在不超过最大楼层数 h h h 的这些楼层里,有多少个楼层和 k k k 除以 x x x 的余数是一样的。把每一类余数对应的满足条件的楼层数都加起来,得到的总数就是我们最终想要的答案,也就是能通过跳楼机到达的不同楼层的数量。
结合上述分析下样例:
15
4 7 9
我们可以分为
x
x
x (||
y
y
y ||
z
z
z)组, 组别为
0
0
0 ~
x
−
1
x - 1
x−1 组,
m
o
d
x
mod x
modx 的余数
0
0
0 组
m
i
n
min
min:
0
0
0 (就是在起点的时候)
1
1
1 组
m
i
n
min
min ;
9
9
9 (9)
2
2
2 组
m
i
n
min
min ;
14
14
14 (7 + 7)
3
3
3 组
m
i
n
min
min ;
7
7
7 (7)
同余最短路应用场景:
同余最短路是一种用于解决特定类型问题的算法技巧,主要应用于以下场景:
- 路径规划与可达性问题
- 经典问题:在一个有向图中,节点代表不同的状态,边代表状态之间的转移,每条边都有一个权值。需要求解从一个起始状态到其他状态的最短路径长度,且这些状态之间可能存在同余关系。
- 实际应用:如在城市交通网络中,某些道路可能只在特定的时间间隔(如每隔 x x x 分钟)开放,就可以将时间作为状态,利用同余最短路来规划在满足时间限制条件下的最短路径。
- 数论与组合问题
- 经典问题:给定若干个正整数 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots,a_n a1,a2,⋯,an 和一个正整数 m m m,需要找出由这些数通过加法运算得到的所有能被 m m m 整除的数,或者求解满足特定同余条件的最小正整数。
- 实际应用:例如在密码学中,需要生成满足特定同余关系的密钥,同余最短路可以用于在给定的数字集合中找到符合要求的数字组合。
- 动态规划优化
- 经典问题:在一些动态规划问题中,状态转移方程可能涉及到对余数的处理,通过同余最短路可以优化状态转移的过程,降低时间复杂度。
- 实际应用:在资源分配问题中,若资源的分配量受到某些条件的限制,且这些限制与余数相关,就可以利用同余最短路来优化动态规划的求解过程,快速找到最优的资源分配方案。
- 货币系统与找零问题
- 经典问题:给定几种不同面值的货币,如 x x x元、 y y y元、 z z z元等,要找出用这些货币组成一定金额的方案,或者判断是否能够组成某些金额,以及计算组成特定金额所需的最少货币数量。
- 实际应用:在自动售货机找零系统中,利用同余最短路可以快速确定用给定的硬币面值组合出顾客所需找零金额的最优方案。
AC code :
#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <queue>
typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<ull, ull> PII;
#define rep(i, n) for(ull i = 0; i < n; i++)
#define Rep(i, len, n) for(ull i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000
const ull INF = std::numeric_limits<ull>::max();
struct edge {
ull v, ne, w;
};
ull n, x, y, z, idx = 0;
std::vector<edge> e;
std::vector<ull> h, dist;
std::vector<bool> vis;
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> q;
inline void Add(ull a, ull b, ull c) {
e[idx] = {b, h[a], c};
h[a] = idx++;
}
inline void Dijkstra() {
q.push({0, 0});
dist[0] = 0;
while(!q.empty()) {
auto[val, u] = q.top();
q.pop();
if(vis[u]) continue;
vis[u] = true;
for(ull i = h[u]; ~i; i = e[i].ne) {
ull v = e[i].v, w = e[i].w;
if(dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
q.push({dist[v], v});
}
}
}
}
int main(void) {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr), std::cout.tie(nullptr);
std::cin >> n >> x >> y >> z;
e.resize(2 * x);
h.resize(x, -1);
vis.resize(x, false);
dist.resize(x, INF);
n--; // 0 ~ 99层
rep(i, x) {
Add(i, (i + y) % x, y);
Add(i, (i + z) % x, z);
}
Dijkstra();
ull ans = 0;
rep(i, x) if(dist[i] <= n) ans += (n - dist[i]) / x + 1;
std::cout << ans << '\n';
return 0;
}
更多的同余最短路:
P2662 牛场围栏
P2371 [国家集训队] 墨墨的等式
AT_arc084_b [ABC077D] Small Multiple
P9140 [THUPC 2023 初赛] 背包