数列的异或和(题解)
题目
样例
- 样例输入
5 5
1 2 3 4 5
1 1 3
1 3 5
0 3 6
1 1 3
1 3 5
- 样例输出
0
2
5
7
二进制运算
- 二进制运算的优先级小于四则运算,所以在与四则运算同时出现时,注意加括号
按位与(&)
在二进制下,相同位的数都为1时,这一位的结果为1,任意一个不是1或都是0,则这一位的结果为0(eg:0100110&0010111=0000110->38&23=6)
- a&1可以用来判断奇偶数(1->奇数;0->偶数)
按位或(|)
在二进制下,相同位的数只要有一个为1,这一位的结果为1(eg:10101|01010=11111->21+10=31)
- 按位或在十进制下不能看做简单的加法
按位异或(∧)
在二进制下,相同位的数值相同,则为0,反之为1(eg:0100110∧0010111=0110001)
- a∧a=0
- a∧0=a
解题思路
首先1e5的数据范围暴力肯定是打不了(纯暴力过40分),所以要用树状数组进行优化(在别的题里叫用树状数组维护前缀和,我觉得在这个题里就叫维护前缀异或吧),下面给出代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=20000100;
int n,m;
int sum[maxn],a[maxn];
//lowbit,add,getsum为树状数组的常规操作
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int y)
{
while(x<=n)
{
sum[x]^=y;
x+=lowbit(x);
}
}
int getsum(int x)
{
int num=0;
while(x>0)
{
num^=sum[x];
x-=lowbit(x);
}
return num;
}
int main()
{
cin >>n>>m;
for(int i=1;i<=n;i++)
{
cin >>a[i];
add(i,a[i]);//给前缀和(前缀异或)赋初值
}
int op,l,r;
for(int i=1;i<=m;i++)
{
cin >>op>>l>>r;
if(op==1)
{
int xx=getsum(r);
int yy=getsum(l-1);
int zz=xx^yy;
cout <<zz<<endl;
}
else if(op==0)
{
int cnt=a[l]^r;
//题目中为将a[x]改为y,所以要先运算一次
add(l,cnt);
a[l]=r;
}
}
return 0;
}