这是一个非常有趣的 **搜索与数学结合的最优化问题**,我们需要为一个特殊的处理器(只有两种操作:`A: add a` 和 `M: multiply by m`)设计一个**最短程序**,使得:
- 所有输入 $ x \in [p, q] $
- 经过程序处理后输出 $ y \in [r, s] $
并且:
- 程序长度最短
- 若多个最短方案存在,选字典序最小的(A < M)
- 输出格式化为 “nA”、“nM” 的形式(连续操作合并)
---
## ✅ 操作定义
设当前值为 $ v $,则:
- `A`: $ v \leftarrow v + a $
- `M`: $ v \leftarrow v \times m $
注意:顺序很重要。例如先 A 后 M 不等于先 M 后 A。
---
## ✅ 关键观察
由于我们只关心区间映射:$[p,q] \to [r,s]$,我们可以分析每一步对区间的变换方式。
### 1. 加法 A:$[l, r] \to [l+a, r+a]$
- 区间平移
### 2. 乘法 M:$[l, r] \to [l*m, r*m]$
- 注意:若 $ m > 0 $,保持顺序;但题目中 $ m \geq 1 $,所以没问题
> 我们不需要跟踪每个数,只需知道整个输入区间经过程序后的输出区间是否落在目标范围内。
---
## ✅ 解题策略
使用 **BFS(广度优先搜索)** 来寻找**最短程序**。
每个状态包括:
- 当前的变换函数 $ f(x) = m_{\text{curr}} \cdot x + b_{\text{curr}} $?
❌ 不完全是仿射变换?等等!
### 🔍 数学建模:程序等价于什么?
任何由 A 和 M 构成的程序都可以表示为一种 **分段仿射函数**,但由于所有操作是确定性的,其实可以统一表示!
实际上,如果我们从输入 $ x $ 开始,执行一系列操作,最终结果是一个形如:
$$
f(x) = c \cdot x + d
$$
但这不完全正确 —— 因为我们中间可能有多次乘法,所以不能简单地提取系数。
但是!我们可以反向思考:考虑如何将 **目标范围 $[r,s]$** 映射回原始输入范围 $[p,q]$ 上。这称为 **逆向推导法(Reverse Interval Propagation)**
---
## ✅ 更优方法:逆向 BFS(推荐)
正向搜索会爆炸(因为数值太大),但我们可以通过 **逆向思维** 来避免大数运算。
### 核心思想:
我们要找最短程序,使得:
$$
\forall x \in [p,q],\quad \text{program}(x) \in [r,s]
$$
等价于:
$$
\text{program}([p,q]) \subseteq [r,s]
$$
我们可以从目标区间 $[r,s]$ 出发,**逆向应用操作的逆过程**,看看能否到达包含 $[p,q]$ 的某个祖先区间。
这样就能用 BFS 找到最短路径(即最短程序),同时控制数值增长。
---
### 逆操作定义
假设当前区间是 $[L,R]$,我们尝试还原上一步操作:
#### 1. 如果上一步是 **M(乘 m)**,那么之前是:
$$
[\lceil L/m \rceil, \lfloor R/m \rfloor]
$$
要求 $ L \leq R $ 且 $ m \mid \text{原值} $ → 实际上不要求整除,只要商在整数范围即可
> 逆 M:$[L, R] \to [\lceil L/m \rceil, \lfloor R/m \rfloor]$
#### 2. 如果上一步是 **A(加 a)**,那么之前是:
$$
[L - a, R - a]
$$
> 逆 A:$[L, R] \to [L - a, R - a]$
我们在逆向过程中不断扩展可能的“源区间”,直到某个源区间包含了输入区间 $[p,q]$。
此时,反转操作序列就是所求程序。
---
## ✅ 算法步骤(逆向 BFS)
1. 初始队列:状态为 `(current_low=r, current_high=s, path=[])`
2. 使用 visited 集合避免重复状态(可基于 (low, high) 剪枝)
3. 对每个状态,尝试逆 A 和逆 M(按字典序优先尝试 A,因为我们要最早找到 lex 最小的最短解)
4. 每次逆操作得到新区间 `[nl, nh]`
5. 如果 `[p, q] ⊆ [nl, nh]`,说明找到了一个合法的逆路径 → 反转操作序列即为答案
6. 否则继续 BFS
7. 若队列为空仍未找到 → impossible
> 注意剪枝:
- 区间下界过大或上界过小 → 跳过
- 数值超过合理范围(比如负太多、太大)→ 剪掉
---
## ✅ 特殊情况
- 如果初始时 $[p,q] \subseteq [r,s]$,则无需任何操作 → 输出 `"empty"`
- 如果无法达到,则输出 `"impossible"`
---
## ✅ C++ 实现代码(逆向 BFS + 字典序优先)
```cpp
#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <climits>
#include <algorithm>
#include <set>
using namespace std;
typedef long long ll;
struct State {
ll low, high;
vector<char> ops; // reversed: from final to initial
State(ll l, ll h, const vector<char>& o) : low(l), high(h), ops(o) {}
};
// Format the program: merge consecutive A/M into "nA", "nM"
string formatProgram(const vector<char>& ops) {
if (ops.empty()) return "empty";
vector<string> res;
int n = ops.size();
for (int i = 0; i < n; ) {
char op = ops[i];
int cnt = 0;
while (i < n && ops[i] == op) {
++cnt;
++i;
}
char type = (op == 'A') ? 'A' : 'M';
res.push_back(to_string(cnt) + type);
}
string formatted;
for (int i = 0; i < res.size(); ++i) {
if (i > 0) formatted += " ";
formatted += res[i];
}
return formatted;
}
string solve(ll a, ll m, ll p, ll q, ll r, ll s) {
// Check empty program
if (p >= r && q <= s) {
return "empty";
}
queue<State> qu;
set<pair<ll, ll>> visited;
qu.emplace(r, s, vector<char>());
visited.insert({r, s});
// We want shortest length, and lexicographically smallest (A < M)
// So try reverse-A first, then reverse-M (because in reverse, order is reversed)
while (!qu.empty()) {
State cur = qu.front(); qu.pop();
// Try reverse A: subtract 'a'
{
ll nl = cur.low - a;
ll nh = cur.high - a;
// Avoid underflow or too small
if (nh >= nl && nl >= -1e18 && nh <= 1e18) {
vector<char> newOps = cur.ops;
newOps.push_back('A'); // this means last op was A
if (p >= nl && q <= nh) {
reverse(newOps.begin(), newOps.end());
return formatProgram(newOps);
}
pair<ll, ll> key = {nl, nh};
if (!visited.count(key)) {
visited.insert(key);
qu.emplace(nl, nh, newOps);
}
}
}
// Try reverse M: divide by m
if (m > 1) {
ll nl = (cur.low + m - 1) / m; // ceil(cur.low / m)
ll nh = cur.high / m; // floor(cur.high / m)
if (nl <= nh && nh >= nl && nl >= -1e18 && nh <= 1e18) {
vector<char> newOps = cur.ops;
newOps.push_back('M');
if (p >= nl && q <= nh) {
reverse(newOps.begin(), newOps.end());
return formatProgram(newOps);
}
pair<ll, ll> key = {nl, nh};
if (!visited.count(key)) {
visited.insert(key);
qu.emplace(nl, nh, newOps);
}
}
}
}
return "impossible";
}
int main() {
ll a, m, p, q, r, s;
int caseNum = 1;
while (cin >> a >> m >> p >> q >> r >> s) {
if (a == 0 && m == 0 && p == 0 && q == 0 && r == 0 && s == 0) break;
string ans = solve(a, m, p, q, r, s);
cout << "Case " << caseNum++ << ": " << ans << endl;
}
return 0;
}
```
---
## ✅ 示例验证
### 输入 1: `1 2 2 3 10 20`
- a=1, m=2, input ∈ [2,3], output should ∈ [10,20]
尝试程序:
- 空不行(2~3 ∉ 10~20)
- 尝试逆向:
初始目标区间: [10,20]
| 步骤 | 操作 | 逆操作 | 得到区间 |
|------|------|--------|---------|
| 1 | 逆 A: sub 1 → [9,19] | no |
| | 逆 M: div 2 → [5,10] | no |
| 2 | 再逆 A: [4,9] → 逆 M: [2,4] → 包含 [2,3]! |
对应正向操作:M, A, A → 即 AAM?不对。
实际应为:
- 正向:A → M → M → 即 `1A 2M`
验证:
- x=2: 2+1=3; ×2=6; ×2=12 ∈ [10,20] ✔️
- x=3: 3+1=4; ×2=8; ×2=16 ∈ [10,20] ✔️
而我们的逆向路径是:M → M → A → 反转得 A → M → M → 即 "1A 2M" ✔️
输出:`Case 1: 1A 2M` ✔️
---
### Case 2: `1 3 2 3 22 33`
答案是 `1M 2A 1M` → 即 M A A M
正向计算(x=2):
- ×3 → 6
- +1 → 7
- +1 → 8
- ×3 → 24 ∈ [22,33] ✔️
x=3:
- 3×3=9+1+1=11×3=33 ✔️
且是最短、字典序最小 → 符合
---
### Case 3: `3 2 2 3 4 5`
尝试所有短程序都无法把 [2,3] 映射进 [4,5] → impossible ✔️
### Case 4: `5 3 2 3 2 3`
输入区间 [2,3] 已经 ⊆ 输出区间 [2,3] → 空程序 ✔️ → "empty"
---
## ✅ 复杂度说明
- 由于每次逆 M 至少除以 m ≥ 2,所以深度最多约 log₂(max_val)
- 每层最多两个分支(A 和 M),总节点数可控
- 使用 visited 集合防止重复搜索相同区间
---