- 本题考查了思维、位运算、数学化简能力。
思路
下面大部分是cf官方题解的原话,因为我认为cf这个题的题解写得确实很好。
取或操作最多只能用一次,因为 a ∣ b ≥ b a~|~b \ge b a ∣ b≥b,所以在使用取或操作之后,我们只能使用第二个操作(自增 b b b)。
如果我们不使用取或操作,答案就是 b − a b - a b−a。
如果我们使用了取或操作。在此之前,我们使用了第一个和第二个操作若干次,令结果值
a
a
a 和
b
b
b 分别为
a
′
a'
a′ 和
b
′
b'
b′(
a
≤
a
′
a \leq a'
a≤a′,
b
≤
b
′
b \leq b'
b≤b′)。在这种情况下,答案将是
(
a
′
−
a
)
+
(
b
′
−
b
)
+
(
(
a
′
∣
b
′
)
−
b
′
)
+
1
=
a
′
+
(
a
′
∣
b
′
)
+
(
1
−
a
−
b
)
(a' - a) + (b' - b) + ((a' \ | \ b') - b') + 1 = a' + (a' \ | \ b') + (1 - a - b)
(a′−a)+(b′−b)+((a′ ∣ b′)−b′)+1=a′+(a′ ∣ b′)+(1−a−b)。由于
(
1
−
a
−
b
)
(1 - a - b)
(1−a−b) 是常数,这等价于使
a
′
+
(
a
′
∣
b
′
)
a' + (a' \ | \ b')
a′+(a′ ∣ b′) 最小。
为了实现这个目标,我们可以递增地枚举
a
′
a'
a′ 从
a
a
a 到
b
b
b 的值。对于每一个
a
′
a'
a′,我们需要使
a
′
∣
b
′
a' \ | \ b'
a′ ∣ b′ 最小,而最佳的
b
′
b'
b′ (首先满足
b
′
≥
b
b'~\ge~b
b′ ≥ b,其次满足
a
′
+
(
a
′
∣
b
′
)
a' + (a' \ | \ b')
a′+(a′ ∣ b′) 最小)可以按照以下方式构造:
将 b ′ b' b′ 设为零,并从高到低迭代位数。有四种情况:
- 如果当前位的 a ′ a' a′ 是 0,且 b b b 是 1,则将 b ′ b' b′ 的当前位设为 1。
- 如果当前位的 a ′ a' a′ 是 0,且 b b b 是 0,则将 b ′ b' b′ 的当前位设为 0。
- 如果当前位的 a ′ a' a′ 是 1,且 b b b 是 1,则将 b ′ b' b′ 的当前位设为 1。
- 如果当前位的 a ′ a' a′ 是 1,且 b b b 是 0,则将 b ′ b' b′ 的当前位设为 1,并停止。
这种构造 b ′ b' b′ 的方法并不好理解。需要想很长时间。我一开始本以为我已经理解了,但当我写这篇博客的时候发现我的理解其实是错误的,直到我写到这里才真正地理解。
C o d e Code Code
#include <bits/stdc++.h>
#define int long long
#define sz(a) ((int)a.size())
#define all(a) a.begin(), a.end()
using namespace std;
using PII = pair<int, int>;
using i128 = __int128;
const int N = 2e5 + 10;
int a, b;
void solve() {
cin >> a >> b;
int res = b - a;
for (int a_ = a; a_ <= b - 1; a_ ++) {
int now_res = 1 - a - b + a_;
int b_ = b;
for (int i = 30; i >= 0; i --) {
if ((a_ >> i & 1) && !(b >> i & 1)) {
b_ = ((b >> i) + 1) << i;
break;
}
}
now_res += a_ | b_;
res = min(res, now_res);
}
cout << " ";
cout << res << "\n";
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
cin >> T; cin.get();
while (T --) solve();
return 0;
}