NC18979 毒瘤xor

题目

题目描述

小a有 \(N\) 个数 \(a_1, a_2, ..., a_N\) ,给出 \(q\) 个询问,每次询问给出区间 \([L, R]\) ,现在请你找到一个数 \(X\) ,使得

  1. \(0 \leq X < 2^{31}\)
  2. \(\sum_{i = L}^R X \oplus a[i]\) 最大,\(\oplus\) 表示异或操作(不懂的请自行百度)

输入描述

第一行一个整数 \(N\) ,表示序列的长度
第二行 \(N\) 个整数,表示序列内的元素
第三行一个整数 \(q\) ,表示询问的个数
接下来 \(q\) 行,每行两个整数 \([L, R]\) ,表示询问的区间

输出描述

输出 \(q\) 行,每行一个整数表示答案

若有多组可行解,请输出较小的解

示例1

输入

5 
4 78 12 1 3
3
2 5
1 4
3 3

输出

2147483632
2147483635
2147483635

备注

对于 \(30\%\)的数据,\(n , q ≤ 10\)
对于 \(60\%\) 的数据,\(n , q ≤ 1000\)

对于 \(100\%\) 的数据,\(n, q ≤ 10^5\)
保证 \(a_i < 2^{31}\)

题解

知识点:贪心,位运算。

显然不能每次枚举,需要更快的贪心方法构造一个 \(X\)

对于位运算的贪心,只需要让每位到达最优就行,因为互相独立。因此,本题只要对于 \(X\) 的每一位产生的和最大即可。

考察所有 \(a_i\) 某一位,要让这位异或和最大,就要考虑这位的 \(1\)\(0\) 的数量,如果 \(1\) 多,则 \(X\) 这位应为 \(0\) ,否则为 \(1\) 。这里可以用二维数组 \(sum[i][j]\) 代表 \([1,i]\) 的数字中第 \(j\) 位是 \(1\) 的数量,前缀和预处理即可。

但要注意的是,我们要求最小的 \(X\) ,因此在两者皆可时 \(0\) 更优,即 \(1\) 的数量为严格小于一半时就用 \(1\)。这里不能用 sum[r][i] - sum[l - 1][i] < (r - l + 1)/2 ,因为这是取下整,要取上整。

时间复杂度 \(O(n+q)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

int a[100007], sum[100007][31];

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) cin >> a[i];
    for (int i = 1;i <= n;i++) {
        for (int j = 0;j < 31;j++) {
            if (a[i] & (1 << j)) sum[i][j]++;
            sum[i][j] += sum[i - 1][j];
        }
    }
    int q;
    cin >> q;
    while (q--) {
        long long l, r;
        cin >> l >> r;
        int x = 0;
        for (int i = 0;i < 31;i++) {
            if (2 * (sum[r][i] - sum[l - 1][i]) < r - l + 1) x |= (1 << i);///这里要除的话取上整
        }
        cout << x << '\n';
    }
    return 0;
}
posted @ 2022-06-16 23:30  空白菌  阅读(57)  评论(0)    收藏  举报