2020牛客寒假算法基础集训营第三场 E.牛牛的随机数【数位DP】

题目链接
题意:从值域[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);

    }


}


猜你喜欢

转载自blog.csdn.net/qq_43653111/article/details/104270298