1 条题解
-
0
C. Boboniu 与位运算 —— 详细题解
题目重述
给定两个序列:
- ()
- ()
对于每个 (),你需要选择一个 (),令:
其中 是按位与运算。
求:
的最小可能值,其中 是按位或运算。
关键观察
观察 1:值域很小
因为 ,所以:
因此,答案 的取值范围是 。
观察 2:OR 运算的性质
设 。
对于任意 , 的二进制表示中,所有为 的位,在 中一定也是 。
反过来, 中为 的位,在所有 中也都必须是 。即:
$$c_i \ \& \ (\sim X) = 0 \quad \text{对所有 } i \text{ 成立} $$等价地:
观察 3:可行性判断
对于固定的 ,如果能对每个 找到一个 ,使得:
则 是一个可行答案。
上式等价于:
即 不能包含任何 中为 的位。
解法一:暴力枚举答案(推荐)
由于答案只有 种可能,从小到大枚举 到 ,检查是否可行。第一个可行的 就是答案。
检查方法
对每个 ,检查是否存在 使得:
等价于:
即 不含有任何 中没有的位。
实现时,可以预先计算 ,然后对每个 判断。
代码实现(解法一)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<int> a(n), b(m); for (int i = 0; i < n; i++) cin >> a[i]; for (int i = 0; i < m; i++) cin >> b[i]; // 从小到大枚举答案 for (int ans = 0; ans < 512; ans++) { bool ok = true; for (int i = 0; i < n; i++) { bool found = false; for (int j = 0; j < m; j++) { if (((a[i] & b[j]) | ans) == ans) { found = true; break; } } if (!found) { ok = false; break; } } if (ok) { cout << ans << '\n'; return 0; } } return 0; }时间复杂度:$O(512 \cdot n \cdot m) \le 512 \times 200 \times 200 = 2 \times 10^7$,可接受。
解法二:逐位贪心(更高效)
从高位到低位确定答案的每一位。
思路
- 初始
- 对于 从 到 (从高到低):
- 尝试将这一位设为 ,即检查 (当前已确定的位保持原样,当前位为 )是否可行
- 如果可行,则这一位保持
- 如果不可行,则这一位必须为 ,即
可行性判断
与解法一相同:对每个 ,检查是否存在 使得 。
代码实现(解法二)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<int> a(n), b(m); for (int i = 0; i < n; i++) cin >> a[i]; for (int i = 0; i < m; i++) cin >> b[i]; int ans = 0; // 从高位到低位贪心 for (int bit = 8; bit >= 0; bit--) { int candidate = ans; // 当前位尝试设为 0 bool ok = true; for (int i = 0; i < n; i++) { bool found = false; for (int j = 0; j < m; j++) { if (((a[i] & b[j]) | candidate) == candidate) { found = true; break; } } if (!found) { ok = false; break; } } if (!ok) { // 当前位必须设为 1 ans |= (1 << bit); } // 如果 ok,当前位保持 0,ans 不变 } cout << ans << '\n'; return 0; }时间复杂度:$O(9 \cdot n \cdot m) \le 9 \times 200 \times 200 = 3.6 \times 10^5$,非常快。
解法三:DP / 位掩码
定义 表示考虑前 个数,能否得到 OR 值为 。
转移
$$dp[i+1][mask \ | \ (a_i \& b_j)] = true \quad \text{若 } dp[i][mask] = true $$初始化
答案
代码实现(解法三)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<int> a(n), b(m); for (int i = 0; i < n; i++) cin >> a[i]; for (int i = 0; i < m; i++) cin >> b[i]; vector<vector<bool>> dp(n + 1, vector<bool>(512, false)); dp[0][0] = true; for (int i = 0; i < n; i++) { for (int mask = 0; mask < 512; mask++) { if (!dp[i][mask]) continue; for (int j = 0; j < m; j++) { int newMask = mask | (a[i] & b[j]); dp[i + 1][newMask] = true; } } } int ans = 511; for (int mask = 0; mask < 512; mask++) { if (dp[n][mask]) { ans = min(ans, mask); } } cout << ans << '\n'; return 0; }时间复杂度:,可接受。
复杂度总结
解法 时间复杂度 空间复杂度 暴力枚举答案 逐位贪心 DP / 位掩码
推荐
- 解法一最简单,不易出错,适合比赛时快速实现
- 解法二最快,适合追求效率
- 解法三最直观,适合理解 DP 思想
- 1
信息
- ID
- 6531
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者