給定 a, b (1≤b≤a≤10¹⁸), k (2≤k≤15)。兩種操作:1) a = a-1;2) 選 x∈[2,k],a = a - (a mod x)。求最少步數將 a 變為 b。
#include
using namespace std;
const int MAXM = 360360; // lcm(1..15)
int dp[MAXM], k;
int vis[MAXM], sp[MAXM];
long long q[MAXM];
int head, tail;
int gcd(int x, int y) { return y ? gcd(y, x%y) : x; }
int dfs(int x) {
if (dp[x] != -1) return dp[x];
int ret = 0x3f3f3f3f;
for (int i = 2; i <= k; i++)
if (x % i) ret = min(ret, dfs(x - x%i));
ret = min(ret, dfs(x-1));
return dp[x] = ret + 1;
}
int bfs(int st, int ed) {
memset(vis, 0, sizeof(vis));
head = tail = 0;
q[tail] = st; sp[tail++] = 0; vis[st] = 1;
while (head < tail) {
long long u = q[head]; int d = sp[head++];
if (u == ed) return d;
for (int i = 2; i <= k; i++) {
long long v = u - u%i;
if (!vis[v]) { vis[v]=1; q[tail]=v; sp[tail++]=d+1; }
}
long long v = u-1;
if (!vis[v]) { vis[v]=1; q[tail]=v; sp[tail++]=d+1; }
}
return -1;
}
int main() {
long long a, b;
while (~scanf("%lld%lld%d", &a, &b, &k)) {
int M = 1;
for (int i = 2; i <= k; i++) M = M*i/gcd(M,i);
memset(dp, -1, sizeof(dp));
dp[0] = 0;
for (int i = 1; i < M; i++) dfs(i);
long long ans = 0;
if (a - a%M > b) {
ans += dp[a%M];
a -= a%M;
}
long long diff = a - b;
long long jump = diff / M;
a -= jump * M;
ans += jump * (1 + dp[M-1]);
diff = a - b;
if (diff > 0) {
if (a % M == 0) ans += bfs((a-1)%M, b%M) + 1;
else ans += bfs(a%M, b%M);
}
printf("%lld\n", ans);
}
return 0;
}
| 技術 | 作用 | 複雜度 |
|---|---|---|
| LCM 分塊 | 將 10¹⁸ 空間壓縮到 360360 狀態空間 | O(M·k) 預處理 |
| DP + BFS 混合 | 大段跳躍用 DP,小段精確用 BFS | O(M) 空間 |
| 模運算性質 | a - a%x 總是向下到 x 的倍數 | 數學基礎 |
a = q·M + r
↓ 處理尾部 r (dp[r] 步)
a = q·M
↓ -1 變為 q·M-1 (1步)
↓ 操作2 系列到 (q-1)·M (dp[M-1] 步)
a = (q-1)·M
... 重複 jump 次 ...
剩餘 < M 部分 → BFS 精確求解
Input: 10 1 4 M = lcm(2,3,4) = 12 dp[10] = 6 路徑: 10→8→6→4→3→2→1 Output: 6 ✓ Input: 1000000000000000000 1 3 M = lcm(2,3) = 6 dp[5] = 3, dp[5] = 3, 1+dp[5]=4 jump = (10¹⁸-1)/6 ≈ 1.666e17 ans = 666666666666666667 ✓
此題展示了如何處理超大數值範圍的最短路問題:利用數學周期性將狀態空間壓縮,預計算小空間最優解,大空間按周期跳躍。LCM 分塊 + DP/BFS 混合是解決此類問題的通用模板。