题意
给你一个数组,然后给你两种选择,1.将x这个位置的值修改为y。2.输出x-y这个区间内的所有子区间的异或和。
思路
假如给你的区间是[2,4],意思是让你求[2,4]的子区间的异或和,那么就是2^3^4^(2^3)^(3^4)^(2^3^4)
,首先我们要知道异或的两个性质:1.a ^ 0 = a 2.a ^ a = a
。那么上面的结果就是:2 ^ 4
(因为有偶数个3,所以异或的结果是0,奇数个2和奇数个4异或的结果就是本身)。我们在来看一个例子:[2,5]的异或和:2^3^4^5^(2^3)^(3^4)^(4^5)^(2^3^4)^(3^4^5)^(2^3^4^5)
,我们将偶数个数的去点,那么结果就是:0
,那么我们会发现一个规律:1.当给的区间的奇偶性相同时,异或的结果是[l,r]中所有于l和r奇偶性相同的位置会贡献奇数次,其他位置贡献的是偶数次,就是0,所以结果是:a_l ^ a_(l+2)……a_r。2.当区间的奇偶性不同的时候,异或的结果就是0。
那么,我们只需要用两个树状数组来保存奇数位置的数和偶数位置的数即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m;
int a[N];
struct BIT
{
int c[N];
int lowbit(int x) { return x & -x; }
void add(int x, int k) { for (int i = x; i <= n; i += lowbit(i)) c[i] ^= k; }
int sum(int x)
{
int sum = 0;
for (int i = x; i; i -= lowbit(i)) sum ^= c[i];
return sum;
}
}tr[2];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) { scanf("%d", &a[i]); tr[i & 1].add(i, a[i]);}
int op;
int x, y;
while (m --)
{
scanf("%d%d%d", &op, &x, &y);
if (op == 1) { tr[x & 1].add(x, a[x] ^ y); a[x] = y; }
else
{
if ((x & 1) ^ (y & 1)) puts("0");
else printf("%d\n", (tr[x & 1].sum(y) ^ tr[x & 1].sum(x - 1)));
}
}
return 0;
}