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; }