对给定数组的 XOR 查询

Posted

技术标签:

【中文标题】对给定数组的 XOR 查询【英文标题】:XOR queries on a given array 【发布时间】:2021-10-15 23:59:31 【问题描述】:

给定一个包含 n 个整数的数组,索引从 1->n。任务是执行 Q 给定的查询,并在每次查询后打印数组的总和

我们可以执行三种类型的操作:

1 X:将 X 添加到数组中(其索引将为 n+1、n+2、...) 2 Y:从数组中移除索引为 Y 的元素 3 Z:对数组中的每个元素 i,执行 i^Z (i xor Z)

示例:

输入

arr[] = 2, 3, 9, 5, 6, 6, Q = 5
1 3
3 5
2 2
3 2
2 7

输出: 34 37 31 27 23

解释

1 3 -> arr[] = 2, 3, 9, 5, 6, 6, 3 -> sum = 34

3 5 -> arr[] = 7, 6, 12, 0, 3, 3, 6 -> sum = 37

2 2 -> arr[] = 7, 12, 0, 3, 3, 6 -> sum = 31

3 2 -> arr[] = 5, 14, 2, 1, 1, 4 -> sum = 27

2 7 -> arr[] = 5, 14, 2, 1, 1 -> sum = 23

P/S:我正在尝试解决段树的问题,但我无法使用 XOR 运算符更新树。有没有其他方法可以解决这个问题?我试图在 O(n.logn) 中解决它

【问题讨论】:

2 7(最后一个查询)如何工作?它似乎正在删除数组的第 6 个元素(而不是第 7 个)。或者更确切地说,我想我的问题是:删除操作是缩小数组还是在操作后数组中有一个“洞”? 不确定为什么需要一棵树。提示:尝试分别计算和的每一位。 @Maurycyt 这也是我正在努力解决的问题,“2 7”查询删除了索引为 7 的元素(不是第 7 个元素),这是第 6 个元素,因为我们删除了索引为 2 的元素 不,单独考虑每个位只会增加常数,因为(很可能)最多需要考虑 64 位 您不需要在每次执行查询时检查每个数字。您只需执行一次,并存储结果。然后您可以非常快速地更新这些结果。 【参考方案1】:

假设您的数字不超过一些标准常数,例如 232 或 264,我们可以通过分别计算位来在常数时间内做到这一点。

您需要:

    记住数组中有多少个数字 记住二进制定位系统中每个位置有多少点亮位。

这是您的示例,扩展为位,最不重要的位在顶部:

2  3  9  5  6  6  3 | sum
-------------------------
0  1  1  1  0  0  1 | 4
1  1  0  0  1  1  1 | 5
0  0  0  1  1  1  0 | 3
0  0  1  0  0  0  0 | 1

现在,这意味着有

4“第一”位点亮 5“第二”位点亮 3“第三”位点亮并 1“第四”位点亮。 号码数量为7。 这些数字的总和是34

我们现在用5 对它进行异或,在二进制中是0101,所以现在会有

7 - 4 = 3“第一”位点亮 5“第二”位点亮 7 - 3 = 4“第三”位点亮 1“第四”位点亮

如果我们总结一下,我们会得到 3 * 2^0 + 5 * 2^1 + 4 * 2^2 + 1 * 2^3 = 37(这里的 ^ 我指的是取幂,而不是 xor)。

所以这就是每次异或操作弹出时你所做的。添加和删​​除数字是很容易的部分,因为您检查它们的位并相应地调整数组中点亮的“i-th”位的计数。

【讨论】:

【参考方案2】:

感谢Maurycyt 我已经解决了这个问题。以下是我的代码,以防有人需要它

const int MAX = 1e5 + 5;
const int MAXBIT = 32;
int n, q, num, xor_add;
int arr[MAX], sum[32];

int getSum()

    int res = 0;
    for(int i = 0; i < MAXBIT; i++)
        res += sum[i]*(1<<i);
    return res;


void updateXor(int x)
    xor_add ^= x;
    for(int i = 0; i < MAXBIT; i++)
        if(x & (1<<i))sum[i] = num - sum[i];


void add(int x)
    ++num;
    arr[n++] = x;
    for(int i = 0; i < MAXBIT; i++)
        if(x & (1<<i))sum[i]++;


void remv(int i)
    --num;
    int x = arr[i-1]^xor_add;
    for(int i = 0; i < MAXBIT; i++)
        if(x & (1<<i))sum[i]--;


int main()

    cin >> n >> q;
    num = n;
    for(int i = 0; i < n; i++)cin >> arr[i];

    for(int i = 0; i < MAXBIT; i++)
        for(int j = 0; j < n; j++)
            if(arr[j] & (1<<i))sum[i]++;

    while(q--)
        int id, x;
        cin >> id >> x;

        if(id == 1)add(x);
        else if(id == 2)remv(x);
        else updateXor(x);

        cout << getSum() << '\n';
    
    return 0;

【讨论】:

以上是关于对给定数组的 XOR 查询的主要内容,如果未能解决你的问题,请参考以下文章

给定一个纬度/经度,从 c# 中的经度/经度列表中找到最近的经度/经度对

如何计算整数中递归地与给定数字相加的数字对

如何对给定的代码应用阿姆达尔定律?

为具有相同结果的给定幂对计算不同项

如何在 React 中对给定的数据进行排序和过滤?

C# 压力测试 - 模拟对给定共享资源的多次访问