2020牛客寒假算法基础集训营3 E 牛牛的随机数

https://ac.nowcoder.com/acm/contest/3004/E

用异或总和除以方案数

考虑每一个二进制位对异或总和的贡献

枚举每一个二进制位

若区间1中这个二进制位是1的数有a1个,是0的数有a0个

区间2中这个二进制位是1的数有b1个,是0的数有b0个

那么这个二进制位的贡献是(a1*b0+a0*b1)* 2^i

如果得到a1,a0,b1,b0呢

可以数位DP

但我用的另外一种方法

假设现在要计算<=n的数中有多少个第i位是1的数

将1—n,每2^i个数划分一段

那么奇数段只有最后1个数是第i位是1

偶数段除了最后1个数第i位都是1

#include<cstdio>

using namespace std;

const int mod=1e9+7;

long long bit[60];

long long get(long long n,int d)
{
    long long m=n/bit[d];
    long long t1=(m+1)/2,t2=m-t1,t3=0;
    if(m&1) t3=n%bit[d];
    return t1+t2*(bit[d]-1)+t3;
}

int pow(int a,int b)
{
    int ans=1;
    for(;b;b>>=1,a=1ll*a*a%mod)
        if(b&1) ans=1ll*ans*a%mod;
    return ans;
}

int main()
{
    int T,inv;
    long long l1,r1,l2,r2,len1,len2,a0,a1,b0,b1,ans;
    bit[0]=1;
    for(int i=1;i<60;++i) bit[i]=bit[i-1]<<1;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
        len1=r1-l1+1;
        len2=r2-l2+1;
        inv=pow((len1%mod)*(len2%mod)%mod,mod-2);
        for(int i=0;i<=59;++i)
        {
             a1=get(r1,i)-get(l1-1,i);
             a0=len1-a1;
             b1=get(r2,i)-get(l2-1,i);
             b0=len2-b1;
             a1%=mod;
             a0%=mod;
             b1%=mod;
             b0%=mod;
        //     printf("%lld\n",a1*b0+a0*b1);
             ans+=(a1*b0+a0*b1)%mod*(bit[i]%mod)%mod;
             ans%=mod;    
        }
        ans=ans*inv%mod; 
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/TheRoadToTheGold/p/12363916.html