题目大意:给定两个数值区间 [(l)(1),(r)(1)]和[(l)(2),(r)(2)],分别从这两个区间随机拿取一个数,求两数异或值的期望。输出以 (P*Q) (-1)((mod) (10)(9) (+) (7)) 下给出,其中 (P) 为所有异或之和,(Q) 为((r)(2)-(l)(2)+1)(*)((r)(1)-(l)(1)+1)。易知求总的异或和即可。
分析:
- 对于两个数异或做贡献,必须满足的是这两数二进制的第 (i) 位上(从二进制末尾开始第一位),一个是 (0) 一个是 (1) ,他们异或后所产生的贡献为 (2)(i-1) 。
比如 6 和 8 的二进制:
(6:0110)
(8:1000)
它们异或做的贡献在第 2 位(6上是 1 ,8 上是 0,贡献为 (2)(1))、第 3 位(6上是 1 ,8 上是 0,贡献为 (2)(2))以及第 4 位(6上是 1 ,8 上是 0,贡献为 (2)(3))。故 6 与 8 异或做的贡献是 (2)(1)(+)(2)(2)(+)(2)(3)(=14。)
所以只要统计二进制的每一位上,第一个区间为 1 的个数与第二个区间为 1 的个数即可。
-
故设第一个区间所有数中二进制第 (i) 位上为 1 的个数为 (S)(1),第二个区间所有数中二进制第 (i) 位上为 1 的个数为 (S)(2)。
以及,第一个区间数的个数为 (sum)(1),第二个区间数的个数为 (sum)(2)。
则二进制第 (i) 位上所做的贡献为:(S)(1)(*)((sum)(2)(-)(S)(2))(+)(S)(2)(*)((sum)(1)(-)(S)(1))。
-
于是我们需要枚举二进制第 (i) 位,然后统计出 (1) ~ (r)(1) 中,二进制第 (i) 位上是 1 的有多少个数。
比如统计 (1) ~ (16) 在二进制第 3 位上是 1 的有多少个数 :(X) (X) (1) (X) (X)
位数:(5) (4) (3) (2) (1)从最高位枚举到第 3 位时,第 3 位如果是 1 则有形如 (0) (0) (1) (X) (X) 和 (0) (1) (1) (X) (X)这两种,然后再分别以这两种形式深搜下去,统计能被组成的数的个数之和。
所以 dfs 需要统计的是,每当 dfs 枚举到前三位中形如 (X) (X) (1) 时,都加上后三位能到达形如(1) (X) (X) 的个数(因为在枚举到第三位(limit)有效时,可能有些(XX1) 并不能枚举到某些的 (1XX),所以并不是简单地相乘),即为答案。
-
比如这里求 (1) ~ (16) 中二进制第 3 位上为 1 的数的个数:从最高位枚举到第 3 位是 1 时的会有 (001、011) 两种,然后发现 (001) 可以到达 (00100、00101、00110、00111),由于这里对 (011)并没有限制,同样也会有(4)种。所以总共有 (8) 种。
-
如果求第 (i) 位,注意当数位枚举到第(i)位时,如果这一位只能到 0 而到不了 1 (因为 (limit)),则不能放进 (for) 循环(具体还是要看怎么写的),否则可能会导致枚举这位是 0 时, (res) 加上了 (0) (X) (X) 的情况,使得答案变大。
代码如下:
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long ll;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 1e18 + 10;
const double eps = 1e-6;
using namespace std;
int T;
int a[65];
ll dp[65][65];
ll l1,r1,l2,r2;
ll dfs(int pos,bool lead,bool limit,int t){
if(!pos) return 1ll;
if(!limit&&!lead&&dp[pos][t]!=-1) return dp[pos][t];
int up=limit?a[pos]:1;
ll res=0;
if(pos==t&&up) res=dfs(pos-1,false,limit,t);
else if(pos!=t){
for(int i=0;i<=up;i++){
if(lead&&i==0) res=(res+dfs(pos-1,true,limit&&i==a[pos],t))%mod;
else res=(res+dfs(pos-1,false,limit&&i==a[pos],t))%mod;
}
}
if(!limit&&!lead) dp[pos][t]=res;
return res;
}
ll solve(ll x,int t){
int pos=0;
while(x){
a[++pos]=x&1;
x/=2ll;
}
if(pos<t) return 0ll;
return dfs(pos,true,true,t);
}
ll qpow(ll x,ll y){
ll ans=1;
while(y){
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y/=2ll;
}
return ans;
}
int main()
{
memset(dp,-1,sizeof(dp));
scanf("%d",&T);
while(T--){
scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
ll res=0;
ll s1,s2,s3,s4;
ll s=(((r1-l1+1ll)%mod)*((r2-l2+1ll)%mod)%mod)%mod;
s=qpow(s,mod-2ll);
ll e=1ll;
for(int i=1;i<=60;i++,e*=2ll){
e%=mod;
s1=(solve(r1,i)-solve(l1-1ll,i)+mod)%mod;
s2=(solve(r2,i)-solve(l2-1ll,i)+mod)%mod;
s3=(s1*((r2-l2+1ll-s2)%mod))%mod;
s4=(s2*((r1-l1+1ll-s1)%mod))%mod;
res=(res+((s3+s4)%mod*e)%mod)%mod;
}
printf("%lld
",(res*s)%mod);
}
}
/*
2
3 5 7 8
1 3 3 5
*/