题解思路:首先观察题目需要我们求的是什么——期望,那我们其实只要算出每一项的贡献,并把他们加起来,最后再除去总数即可。那么这么大的数据范围怎么算每一项的贡献呢?这里就需要用到数位dp了。由于题目要求的是异或值的期望,因此二进制的数位dp是最好的选择,我们只需要将其拆位就能得到每一位是1或是0的数量。最终答案其实也就是∑(cnta0*cntb1+cnta1*cntb0)*(1<<i),这里面的cnta0代表的是a区间,第i位为0的数量,以此类推,答案就很显然了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
#define ls l,mid,rt<<1
#define rs mid+1,r,rt<<1|1
#define endl '
'
const int MAXN = 1e6+10;
const double EPS = 1e-12;
const ll mod = 1e9+7;
int T;
ll l1,r1,l2,r2;
ll dp[70][70][2],a[70];
ll dfs(int pos,int sta,int k,bool limit){
if(pos==0)return sta;
if(!limit&&dp[pos][k][sta]!=-1)return dp[pos][k][sta];
int up=limit ? a[pos] : 1;
ll ans=0;
for(int i=0;i<=up;i++)
ans+=dfs(pos-1,sta||(pos==k&&i!=0),k,limit&&i==a[pos]);
if(!limit)dp[pos][k][sta]=ans;
return ans;
}
ll solve(ll x,int k){
int pos=0;
while(x){
a[++pos]=x%2;
x/=2;
}
return dfs(pos,0,k,1);
}
ll poww(ll a,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
int main()
{
scanf("%d",&T);
while(T--){
memset(dp,-1,sizeof(dp));
scanf("%lld %lld %lld %lld",&l1,&r1,&l2,&r2);
ll now=1,ans=0;
for(int i=1;i<70;i++){
ll ca0=solve(r1,i)-solve(l1-1,i),ca1=r1-l1+1-ca0;
ll cb0=solve(r2,i)-solve(l2-1,i),cb1=r2-l2+1-cb0;
ca1%=mod,ca0%=mod,cb1%=mod,cb0%=mod;
ans=(ans+(ca1*cb0%mod+ca0*cb1%mod)%mod*now%mod)%mod;
now=now*2%mod;
}
ll x=(r1-l1+1)*(r2-l2+1)%mod;
cout<<ans*poww(x,mod-2)%mod<<endl;
}
}