跳楼机

从来没见过同余最短路,确实有点意思的。此类题关键在于,其中某个元素的范围很小,我们需要以此为切入点解决。方便起见,我们从 开始出发,存在的最后一个楼层为

对于任何一个能到达的数,可以继续乘坐跳楼机上升 层若干次。因此可以发现,设有 ,其中 ,若可以到达 ,则一定可以到达

假设我们已经求出了仅使用 能到达的数集 。从 中任取一数 ,则任意 都可以达到,这样的 个。

但是,一个数可能会被重复统计。这是由于 中可能有多个数关于 同余。其中一个解决方案,即多个同余的数仅保留最小的,这是因为从最小的这个数 开始一直加 若干次后,一定可以到达其他与 同余的点。

现在,对于每个 ,我们想要知道集合中满足 的最小的点 (形式化地,我们要求 )。这里的一个相当聪明的步骤是转化为图论问题。

我们建立一个 个点的图,编号为 。对某一个点 ,我们建立边 。在这个图中,每条种移动方案都可以用一条路径描述:上升 层相当于沿着边权为 的移动一轮,上升 层相当于沿着边权为 的移动一轮,移动的距离即到达的高度。

出发跑最短路,设从 出发到点 的最短路为 。而 同时还是我们上面要求的

确实很聪明的转化。

#include <bits/extc++.h>
using namespace std;
namespace pbds = __gnu_pbds;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
vector<uli> dijkstra(size_t s, vector<vector<pair<size_t, uli>>> const& mp) {
  vector<uli> dis(mp.size(), numeric_limits<uli>::max() / 2);
  vector<bool> vis(mp.size());
  priority_queue<pair<uli, size_t>, vector<pair<uli, size_t>>,
                 greater<pair<uli, size_t>>>
      q;
  dis[s] = 0;
  q.emplace(0, s);
  while (!q.empty()) {
    size_t p = q.top().second;
    q.pop();
    if (vis[p]) continue;
    vis[p] = true;
    for (auto i : mp[p])
      if (dis[p] + i.second < dis[i.first])
        q.emplace(dis[i.first] = dis[p] + i.second, i.first);
  }
  return dis;
}
int main(void) {
  ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
  uli h;
  ui x, y, z;
  fin >> h >> x >> y >> z;
  --h;
  vector<vector<pair<size_t, uli>>> mp(x);
  for (size_t i = 0; i < x; ++i)
    mp[i].emplace_back((i + y) % x, y), mp[i].emplace_back((i + z) % x, z);
  auto dis = dijkstra(0, mp);
  uli ans = 0;
  for (size_t i = 0; i < x; ++i)
    if (dis[i] <= h) ans += (h - dis[i]) / x + 1;
  fout << ans;
  return 0;
}
posted @ 2024-09-24 15:24  MrPython  阅读(7)  评论(0)    收藏  举报  来源