题目描述
牛牛和牛可乐是一对好朋友,现在牛牛从值域[l1,r1]中随机给出一个数字a,牛可乐从值域[l2,r2]中随机给出一个数字b。问你 的数学期望。其中 为位运算符,表示按位取异或。
为了避免你输出的答案出现精度误差,请你输出一个分数 ,其中 表示在mod 条件下的乘法逆元。数据保证 ,也就是说保证 在该模条件下有意义,并且保证 不是mod的倍数。
输入描述:
第一行是一个正整数 表示有T组案例。
接下来T行,每行四个正整数
输出描述:
请输出期望
输入
2
3 5 7 8
1 3 3 5
输出
500000011
222222228
说明
a可取3,4,5,b可取7,8
共6种情况,答案为
备注:
乘法逆元:
题解
- 首先答案为 ,就是枚举每种情况,然后除以方案总数。
- 首先考虑二进制的个位产生的贡献,那么二进制个位产生的贡献有两部分:
- 区间内个位为0的数字乘以 区间内个位为1的数字。
-
区间内个位为1的数字乘以
区间内个位为0的数字。
做一个二进制位的数位DP,用于统计该数位0与1的个数。 - 假设
中二进制的个位产生0的数目为
二进制的个位产生1的数目为
中二进制的个位产生0的数目为 二进制的个位产生1的数目为
那么就产生了 的贡献。 - 如果是二进制的十位呢,那么就是 。
- 那么二进制的第k位的贡献就为
- 不知道题解DP的是什么,待补
AC-Code
#include<bits/stdc++.h>
using namespace std;
const long long mod=(int)1e9+7;
int i,i0,T;
long long cnta[70],cntb[70],dp[70][70][2],a[70];
long long dfs(int len,bool maxi,int k,bool f)
{
if(dp[len][k][f]!=-1&&maxi==0)return dp[len][k][f];
long long cnt=0;
if(!len)return f;
int maxn=maxi?a[len]:1;
for(int i=0;i<=maxn;i++)cnt+=dfs(len-1,maxi&&i==a[len],k,f||len==k&&i);
return maxi?cnt:dp[len][k][f]=cnt;
}
long long div(long long tmp,int k)
{
memset(a,0,sizeof(a));
int p=0;
while(tmp)a[++p]=tmp%2,tmp/=2;
return dfs(p,1,k,0);
}
long long inv(long long x,long long mod)
{
long long k=mod-2,ans=1;
while(k)
{
if (k&1) ans=ans*x%mod;
x=x*x%mod;
k>>=1;
}
return ans;
}
int main()
{
memset(dp,-1,sizeof(dp));
scanf("%d",&T);
while(T--)
{
long long l1,r1,l2,r2,p=1,ans=0;
scanf("%lld %lld %lld %lld",&l1,&r1,&l2,&r2);
l1--,l2--;
for(i=1;i<=60;i++,p*=2)
{
cnta[i]=div(r1,i)-div(l1,i);
cntb[i]=div(r2,i)-div(l2,i);
ans+=(cnta[i]%mod*((r2-l2-cntb[i])%mod)%mod+cntb[i]%mod*((r1-l1-cnta[i])%mod)%mod)*(p%mod)%mod;
ans%=mod;
}
ans%=mod,ans+=mod,ans%=mod;
ans=ans*inv(((r1-l1)%mod)*((r2-l2)%mod)%mod,mod)%mod;
printf("%lld\n",ans);
}
return 0;
}