2025“钉耙编程”中国大学生算法设计春季联赛(2)1001
\(赛时想得太过复杂想出了前半段解法 但是后半段查询认为需要一个类似于dp的操作 实际上对于|操作进行一个倒序的查询就可以得到最优解了\)
赛时想出了一半的解法
实际上为了解决这个问题,我们需要找到一个优雅集合 S 中的最小自然数 y,使得 y ≥ x,并且 y 一定属于 S。优雅集合 S 满足包含 0 和 2^64 - 1,并且在按位与和按位或操作下封闭。
方法思路
- 预处理每个位的掩码:对于每个二进制位 b,计算所有已知元素中该位为 1 的元素的按位与。这个掩码表示如果某个数在 b 位为 1,则必须包含该掩码的所有位。
- 处理每个查询:对于每个查询 x,从高位到低位遍历每一位。如果当前位在 x 中为 1,则必须包含该位的掩码。如果当前位为 0,则尝试将该位设为 1,并计算可能的候选值。最终选择最小的候选值作为结果。
解决代码
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
const ull M = 100000000000001029ULL;
const ull mx = ~0ULL;
void solve() {
int n, q;
cin >> n >> q;
vector<ull> bit(64, mx);
vector<ull> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
for (int b = 0; b < 64; ++b) {
if (a[i] & (1ULL << b)) {
bit[b] &= a[i];
}
}
}
ull res = 0;
while (q--) {
ull x;
cin >> x;
ull cur = 0, ans = mx;
for (int b = 63; b >= 0; --b) {
if (x & (1ULL << b)) {
cur |= bit[b];
} else {
ans = min(ans, cur | bit[b]);
}
}
ans = min(ans, cur);
res ^= ans % M;
}
cout << res << '\n';
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int T;
cin >> T;
while (T--) solve();
return 0;
}
代码解释
- 预处理掩码:对于每个二进制位 b,计算所有已知元素中该位为 1 的元素的按位与,存储在
bit数组中。 - 处理查询:遍历每个查询的每一位,从高位到低位。如果当前位在 x 中为 1,则累积该位的掩码到
cur中。如果当前位为 0,则尝试将该位设为 1,并计算可能的候选值。最终选择最小的候选值作为结果,并对结果取模和异或处理。
这种方法确保了在处理每个查询时,能够高效地找到满足条件的最小 y,并利用了按位操作的性质来优化计算。

浙公网安备 33010602011771号