PIPIOJ 1439: PIPI的位运算问题Ⅱ 位运算 异或和

http://pipioj.online/problem.php?id=1439
在这里插入图片描述
思路:如果我们能够快速计算出 [ 0 , n ] [0,n] [0,n]内所有数的二进制第 i i i位(假设从低位开始,下标从1开始)上 1 1 1的总和,那么依据前缀和的性质就可以在 O ( 1 ) O(1) O(1)时间内得到 [ L , R ] [L,R] [L,R]内所有数的二进制第 i i i位上 1 1 1的总和,依据异或的性质我们知道,如果总和是奇数,那么这一位为 1 1 1否则为 0 0 0。那么如何快速计算总和呢?我们写下前八个数的二进制表示: 000 、 001 、 010 、 011 、 100 、 101 、 110 、 111 000、001、010、011、100、101、110、111 000001010011100101110111。不难发现每一位的出现都是有规律的:第 i i i位的循环节长度为 2 i 2^i 2i,循环节前 2 i − 1 2^{i-1} 2i1个数是 0 0 0,后 2 i − 1 2^{i-1} 2i1个数是 1 1 1,那么从 0 0 0 x − 1 x-1 x1的二进制第 i i i位上 1 1 1的总和就等于: x / 2 i ∗ 2 i − 1 + m a x ( 0 , x % 2 i − 2 i − 1 ) x/2^i*2^{i-1}+max(0,x\%2^i-2^{i-1}) x/2i2i1+max(0,x%2i2i1)。那么就可以在 O ( l o g R ) O(logR) O(logR)内算出结果。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

using ll=long long;

ll L,R;

int main()
{
    
    
    scanf("%lld%lld",&L,&R);
    ll ans=0,i=1,i2=i<<1;
    ++R;
    while(R>=i)
    {
    
    
        ll vr=R/i2*i+max(0ll,R%i2-i);
        ll vl=L/i2*i+max(0ll,L%i2-i);
        if((vr-vl)&1)
            ans|=i;
        i=i2;
        i2<<=1;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiji333/article/details/114468032