[CSP-S模拟测试]:异或(数学)

题目描述

给定$L,R$,我们希望你求出:
$$\sum\limits_{i=L}^R\sum\limits_{j=L}^R(i\oplus j)$$
其中这里的$\oplus$表示异或运算。
答案对$10^9+7$取模。


输入格式

第一行一个整数$T$,表示数据组数。
接下来$T$行,每行两个整数$L,R(0\leqslant L\leqslant R\leqslant 10^9)$,描述一组数据。


输出格式

每组数据输出一行一个整数,表示答案。


样例

样例输入:

2
1 2
0 1023

样例输出:

6
536346624


数据范围与提示

样例解释:

第一组数据:$1\oplus 1=2\oplus 2=0,1\oplus 2=3$。

数据范围:

对$100\%$的数据,$T\leqslant 50$。
$\bullet$子任务$1$($20$分):保证$L,R\leqslant 1,000$。
$\bullet$子任务$2$($30$分):保证$(R−L)\leqslant 10^6$。
$\bullet$子任务$3$($10$分):保证$L=0,R=2^k−1$。
$\bullet$子任务$4$($40$分):无特殊限制。


题解

又没打正解,讲一下我的做法。

其实答案就是二进制位下每一位$1$的个数乘每一位$0$的个数乘$1<<$当前位数。

那么考虑如何快速求出每一位$1$和$0$的个数。

把每一个数拆成二进制位,如下$\downarrow$

0:0000
1:0001
2:0010
3:0011
4:0100
5:0101
6:0110
7:0111

那么我们会惊喜的发现第$i$位会呈一个$2\times i$的循环节,先是$i$个$0$,之后是$i$个$1$。

算出$0\sim R$中$0$和$1$的个数再减去$0\sim L$的即可,这样就能算出每一位$0$和$1$的个数了,也就求出了答案。

时间复杂度:$\Theta(30\times T)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
long long L,R;
long long ans;
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		ans=0;
		scanf("%lld%lld",&L,&R);
		for(long long i=1;i<=30;i++)
		{
			long long flag=(R/(1<<i)-(L-1)/(1<<i))<<(i-1);
			if(R%(1<<i)-(1<<(i-1))+1>0)flag+=R%(1<<i)-(1<<(i-1))+1;
			if((L-1)%(1<<i)-(1<<(i-1))+1>0)flag-=(L-1)%(1<<i)-(1<<(i-1))+1;
			ans=(ans+flag*(R-L+1-flag)%mod*(1<<i)%mod)%mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

rp++

相关推荐