题目链接
题意:从值域[l2,r2]中随机给出一个数字b。问你a⊕ b的数学期望。
思路:首先要看每个区间的数字每位的0和1数量,每位对答案的贡献就是cnt1* cnt0*(1<<(i-1));那么我们怎么求区间内每位上0和1的个数呢?
1.直接拆位数位dp,dp[i][j]代表枚举到第i位当前是j的1的方案数。
2.还有一种拆位位运算算1-n内一共有多少1,不用数位dp
取模少加个()看了半小时,我服我自己,又是取模
ll getnum0(ll n,ll k)
{
++n;//因为这种计算方式从0开始
ll a=1ll<<k,b=1ll<<(k+1);
ll ans=n/b*a+min(n%b,a);
return ans;
}
LL getnum0(LL n,LL w)
{
return (n/(w<<1)*w%mod+max(0LL,n%(w<<1)-w+1)%mod)%mod;
}
void get1(LL n,LL arr[]) /**< 求1~n所有数各个位上1的个数之和 */
{
for(int i=1; i<=64; ++i)
arr[i]=0;
for(LL i=1,f=2,b=1; b<=n; ++i,f<<=1,b<<=1)
{
arr[i]=(n/f)*(f/2); /**< 对于每一位,1~n可分为n/t组(t是每组的01数量),其中每组有t/2个是1 */
if(n%f>=b)
arr[i]+=(n%f-b+1); /**< 加上余数部分 */
}
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int mod=1e9+7;
ll l1,r1,l2,r2;
ll dp[70][3],a[70];
ll dfs(ll now,ll k,ll limit)
{
if(!now)
return 1;
if(!limit&&~dp[now][limit])
return dp[now][limit];
ll up=limit?a[now]:1,sum=0;
for(ll i=0; i<=up; i++)
{
if(now==k&&i==0)
continue;
sum+=dfs(now-1,k,limit&&i==up);
}
if(!limit)
dp[now][limit]=sum;
return sum;
}
ll solve(ll num,ll k)
{
if((1ll<<(k-1))>num)
return 0;
ll cnt=0;
while(num)
{
a[++cnt]=num%2;
num=num/2;
}
memset(dp,-1,sizeof dp);
return dfs(cnt,k,1);
}
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=(ans%mod*(a%mod))%mod;
a=(a%mod*(a%mod))%mod;
b>>=1;
}
return ans%mod;
}
ll inv(ll a)
{
return qpow(a,mod-2)%mod;
}
int main()
{
ll t;
cin>>t;
while(t--)
{
ll ans=0;
scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
ll di=((r2-l2+1)%mod*((r1-l1+1)%mod)%mod)%mod;
for(ll i=1; i<=64; i++)
{
ll cnt11=solve(r1,i)-solve(l1-1,i);
ll cnt21=solve(r2,i)-solve(l2-1,i);
ll cnt10=(r1-l1+1)-cnt11;
ll cnt20=(r2-l2+1)-cnt21;
cnt11%=mod,cnt21%=mod,cnt10%=mod,cnt20%=mod;
ll now=(cnt10*cnt21%mod+cnt11*cnt20%mod)%mod;
ans=(ans+(now*((1ll<<(i-1))%mod))%mod)%mod;
}
ans=(ans%mod)*(inv(di)%mod)%mod;
printf("%lld\n",ans);
}
}