2025“钉耙编程”中国大学生算法设计春季联赛(2)1001

\(赛时想得太过复杂想出了前半段解法 但是后半段查询认为需要一个类似于dp的操作 实际上对于|操作进行一个倒序的查询就可以得到最优解了\)
赛时想出了一半的解法
实际上为了解决这个问题,我们需要找到一个优雅集合 S 中的最小自然数 y,使得 y ≥ x,并且 y 一定属于 S。优雅集合 S 满足包含 0 和 2^64 - 1,并且在按位与和按位或操作下封闭。

方法思路

  1. 预处理每个位的掩码:对于每个二进制位 b,计算所有已知元素中该位为 1 的元素的按位与。这个掩码表示如果某个数在 b 位为 1,则必须包含该掩码的所有位。
  2. 处理每个查询:对于每个查询 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;
}

代码解释

  1. 预处理掩码:对于每个二进制位 b,计算所有已知元素中该位为 1 的元素的按位与,存储在 bit 数组中。
  2. 处理查询:遍历每个查询的每一位,从高位到低位。如果当前位在 x 中为 1,则累积该位的掩码到 cur 中。如果当前位为 0,则尝试将该位设为 1,并计算可能的候选值。最终选择最小的候选值作为结果,并对结果取模和异或处理。

这种方法确保了在处理每个查询时,能够高效地找到满足条件的最小 y,并利用了按位操作的性质来优化计算。

posted @ 2025-03-16 21:48  archer2333  阅读(71)  评论(0)    收藏  举报