newCoder Wannafly挑战赛4:B-小AA的数列 (位运算)

题目链接:https://www.nowcoder.com/acm/contest/35/B


题目给出N个数字,然后求区间长度在【L,R】内且为偶数的所有子

区间的异或和。


看别人的代码看了好久才明白过来。就是按照二进制位的贡献来进行计算。

对于一个【l,r】的区间,如果其第i个2进制位(从低位往高位数)如果有奇数

个1,则异或后第i个二进制位为1,对答案贡献2^i。为了可以方便的求的

任意区间内所有元素的异或值,我们可以先进行前缀异或,即假如有数组
a,则a[i]代表前i个数字异或后的值,则如果求XOR【L,R】有如下式子:

XOR【L,R】  = a[R] xor a[R-L].   原因是异或满足交换率,相同的数字异或后

为0,而0异或上任何数都为其本身。

因此求[L,R】区间数字的第i个二进制位的异或值“

XOR(bit i)[L,R] = (bit i) a[R] xor (bit i)a[R-L].

如果欲让其对结果有贡献,则如果a[R]的第i位为1,则a[R-L]的第i位应该为0.

如果a[R]的第i位为0,则a[R-L]的第i为应该为1.

而这个题目需要的区间是偶数长度的区间

因此如果j是奇数,则以j为右边界的满足条件的区间的左边界肯定都是奇数。

如果j是偶数,则以j为右边界的满足条件的区间的左边界肯定都是偶数。

因此为了计算满足以第j个数字为结尾的符合条件的区间其第i位的贡献,我们

需要分奇数和偶数。

因为j分了奇偶,区间异或值的第i位也有0,1之分,状态组合为4种。



AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

const int mod = 1e9+7;
const int maxn = 1e5+10;
typedef long long LL;
int num[maxn];
int cnt[2][2];
int main() {
    int N,L,R;
    while(~scanf("%d%d%d",&N,&L,&R)) {
        num[0] = 0;
        for(int i = 1; i <= N; i++) {
            scanf("%d",&num[i]);
            num[i] = num[i]^num[i-1];   ///求前缀异或
        }
        ///L是奇数,L++
        if(L&1) L++;
        ///R是奇数,R--
        if(R&1) R--;
        if(R < L) {
            printf("0\n");
        }
        else {
            LL sum = 0;
            ///求第i位的贡献,第i位每有一个位位1,则贡献2^i
            for(int i = 0; i < 32; i++) {
                int ans = 0;
                memset(cnt,0,sizeof(cnt));
                for(int j = L; j <= N; j++) {
                    /**
                    cnt[0][0]代表,j是偶数,且前J-L个值异或后,其第i个二进制位为0
                    cnt[0][1]代表,j是偶数,且前J-L个值异或后,其第i个二进制位为1
                    cnt[1][0]代表,j是奇数,且前J-L个值异或后,其第i个二进制位为0
                    cnt[1][1]代表,j是奇数,且前J-L个值异或后,其第i个二进制位为1
                    */
                    cnt[j&1][(num[j-L]>>i)&1]++;
                    ans = (ans + cnt[j&1][!((num[j]>>i)&1)])%mod;
                    ///J>=R的时候,减去多余的部分
                    if(j >= R) {
                        cnt[j&1][(num[j-R]>>i)&1]--;
                    }
                }
                LL power = 1;
                power = (power<<i)%mod;   ///第i个二进制权值为2^i
                sum = (sum + (ans*power)%mod)%mod;
            }
            printf("%lld\n",sum);
        }
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值