zoukankan      html  css  js  c++  java
  • 2020牛客寒假集训营3 E题 牛牛的随机数(数位dp)

    ### 题目链接 ###

    题目大意:给定两个数值区间 [(l)(1)(r)(1)]和[(l)(2)(r)(2)],分别从这两个区间随机拿取一个数,求两数异或值的期望。输出以 (P*Q) (-1)((mod) (10)(9) (+) (7)) 下给出,其中 (P) 为所有异或之和,(Q) 为((r)(2)-(l)(2)+1)(*)((r)(1)-(l)(1)+1)。易知求总的异或和即可。


    分析:

    • 对于两个数异或做贡献,必须满足的是这两数二进制的第 (i) 位上(从二进制末尾开始第一位),一个是 (0) 一个是 (1) ,他们异或后所产生的贡献为 (2)(i-1)
      比如 6 和 8 的二进制:
      (6:0110)
      (8:1000)
      它们异或做的贡献在第 2 位(6上是 1 ,8 上是 0,贡献为 (2)(1))、第 3 位(6上是 1 ,8 上是 0,贡献为 (2)(2))以及第 4 位(6上是 1 ,8 上是 0,贡献为 (2)(3))。故 6 与 8 异或做的贡献是 (2)(1)(+)(2)(2)(+)(2)(3)(=14。)
      所以只要统计二进制的每一位上,第一个区间为 1 的个数与第二个区间为 1 的个数即可。

    • 故设第一个区间所有数中二进制第 (i) 位上为 1 的个数为 (S)(1),第二个区间所有数中二进制第 (i) 位上为 1 的个数为 (S)(2)
      以及,第一个区间数的个数为 (sum)(1),第二个区间数的个数为 (sum)(2)
      则二进制第 (i) 位上所做的贡献为:(S)(1)(*)((sum)(2)(-)(S)(2))(+)(S)(2)(*)((sum)(1)(-)(S)(1))。

    • 于是我们需要枚举二进制第 (i) 位,然后统计出 (1) ~ (r)(1) 中,二进制第 (i) 位上是 1 的有多少个数。
      比如统计 (1) ~ (16) 在二进制第 3 位上是 1 的有多少个数 :

               (X) (X) (1) (X) (X)
      位数:(5)  (4)  (3)  (2)  (1)

      从最高位枚举到第 3 位时,第 3 位如果是 1 则有形如 (0) (0) (1) (X) (X)(0) (1) (1) (X) (X)这两种,然后再分别以这两种形式深搜下去,统计能被组成的数的个数之和。
      所以 dfs 需要统计的是,每当 dfs 枚举到前三位中形如 (X) (X) (1) 时,都加上后三位能到达形如(1) (X) (X) 的个数(因为在枚举到第三位(limit)有效时,可能有些(XX1) 并不能枚举到某些的 (1XX),所以并不是简单地相乘),即为答案。

    • 比如这里求 (1) ~ (16) 中二进制第 3 位上为 1 的数的个数:从最高位枚举到第 3 位是 1 时的会有 (001、011) 两种,然后发现 (001) 可以到达 (00100、00101、00110、00111),由于这里对 (011)并没有限制,同样也会有(4)种。所以总共有 (8) 种。

    • 如果求第 (i) 位,注意当数位枚举到第(i)位时,如果这一位只能到 0 而到不了 1 (因为 (limit)),则不能放进 (for) 循环(具体还是要看怎么写的),否则可能会导致枚举这位是 0 时, (res) 加上了 (0) (X) (X) 的情况,使得答案变大。

    代码如下:

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<string>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    typedef long long ll;
    const ll mod = 1e9 + 7;
    const int inf = 0x3f3f3f3f;
    const ll INF = 1e18 + 10;
    const double eps = 1e-6;
    using namespace std;
    int T;
    int a[65];
    ll dp[65][65];
    ll l1,r1,l2,r2;
    ll dfs(int pos,bool lead,bool limit,int t){
    	if(!pos) return 1ll;
    	if(!limit&&!lead&&dp[pos][t]!=-1) return dp[pos][t];
    	int up=limit?a[pos]:1;
    	ll res=0;
    	if(pos==t&&up) res=dfs(pos-1,false,limit,t);
    	else if(pos!=t){
    		for(int i=0;i<=up;i++){
    			if(lead&&i==0) res=(res+dfs(pos-1,true,limit&&i==a[pos],t))%mod;
    			else res=(res+dfs(pos-1,false,limit&&i==a[pos],t))%mod;
    		}
    	}
    	if(!limit&&!lead) dp[pos][t]=res;
    	return res;
    }
    ll solve(ll x,int t){
    	int pos=0;
    	while(x){
    		a[++pos]=x&1;
    		x/=2ll;
    	}
    	if(pos<t) return 0ll;
    	return dfs(pos,true,true,t);
    }
    ll qpow(ll x,ll y){
    	ll ans=1;
    	while(y){
    		if(y&1) ans=(ans*x)%mod;
    		x=(x*x)%mod;
    		y/=2ll;
    	}
    	return ans;
    }
    int main()
    {
    	memset(dp,-1,sizeof(dp));
    	scanf("%d",&T);
    	while(T--){
    		scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
    		ll res=0;
    		ll s1,s2,s3,s4;
    		ll s=(((r1-l1+1ll)%mod)*((r2-l2+1ll)%mod)%mod)%mod;
    		s=qpow(s,mod-2ll);
    		ll e=1ll;
    		for(int i=1;i<=60;i++,e*=2ll){
                e%=mod;
    			s1=(solve(r1,i)-solve(l1-1ll,i)+mod)%mod;
    			s2=(solve(r2,i)-solve(l2-1ll,i)+mod)%mod;
    			s3=(s1*((r2-l2+1ll-s2)%mod))%mod;
    			s4=(s2*((r1-l1+1ll-s1)%mod))%mod;
    			res=(res+((s3+s4)%mod*e)%mod)%mod;
    		}
    		printf("%lld
    ",(res*s)%mod);
    	}
    }
    
    /*
    2
    3 5 7 8
    1 3 3 5
    */
    
  • 相关阅读:
    worldWind发布1.3.2版本了
    XMLSerializer中数组对象的设定
    IE6+UTF8的一个怪异问题
    恢复ServU管理员密码方法
    asp.net中的窗体身份验证(不同的角色访问不同的目录)
    什么是 Landing Page?
    如何让排名更加稳定
    JS替换空格回车换行符
    外部调用ZBLOG文章的方法
    表单填写字母时大小写自动互转(CSS方式)
  • 原文地址:https://www.cnblogs.com/Absofuckinglutely/p/12291015.html
Copyright © 2011-2022 走看看