数列的异或和(题解)

题目

样例

  • 样例输入
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;
}
posted @ 2024-02-18 21:21  藦兲轮の约顁  阅读(43)  评论(0编辑  收藏  举报