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
000、001、010、011、100、101、110、111。不难发现每一位的出现都是有规律的:第
i
i
i位的循环节长度为
2
i
2^i
2i,循环节前
2
i
−
1
2^{i-1}
2i−1个数是
0
0
0,后
2
i
−
1
2^{i-1}
2i−1个数是
1
1
1,那么从
0
0
0到
x
−
1
x-1
x−1的二进制第
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/2i∗2i−1+max(0,x%2i−2i−1)。那么就可以在
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;
}