题目大意:给定两个数值区间 [\(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\n",(res*s)%mod);
}
}
/*
2
3 5 7 8
1 3 3 5
*/