E 牛牛的随机数
题解:思路就是算二进制每个位置的贡献,然后再将每个位置的贡献加起来就是总贡献。
关键是数位dp咋写;我看了半天就是唯一想不通的地方就是为啥要
lim == 0 时才能记忆化转移,同样才能给dp赋值。因为当lim==1时说明这个位置有限制所以你枚举的那个数位的数字可能就不是1,就可能多加贡献。所以需要没有限制的时候那么你枚举的数位必然可以达到1,所以可以无脑转移。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 1e9+7;
ll dp[100][100][2];
int a[100];
ll dfs(int pos,int k,int lim,int f)
{
if(dp[pos][k][f]!=-1&&lim==0) return dp[pos][k][f];
if(!pos) return 1LL*f;
ll ans = 0;
int up = lim?a[pos]:1;
for(int i=0;i<=up;i++)
ans += dfs(pos-1,k,lim&&i==up,f||pos==k&&i);
return lim?ans:dp[pos][k][f] = ans;
}
ll div(ll x,int k)
{
memset(a,0,sizeof(a));
int pos = 0;
while(x) { a[++pos] = x%2; x/=2; }
return dfs(pos,k,1,0);
}
ll inv(ll x)
{
ll ans = 1LL;
ll tmp = mod - 2;
while(tmp)
{
if(tmp&1) ans = ans*x%mod;
x = x*x % mod;
tmp/=2;
}
return ans;
}
int main()
{
int T; scanf("%d",&T);
memset(dp,-1,sizeof(dp));
while(T--)
{
ll p =1LL,ans = 0;
ll l1,r1,l2,r2; scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
for(int i=1;i<=60;i++,p*=2)
{
ll x , y;
x = div(r1,i) - div(l1-1,i);
y = div(r2,i) - div(l2-1,i);
ans += ( ( x % mod ) *((r2-l2+1-y) % mod) % mod + (y%mod) * ( (r1-l1+1-x) % mod ) % mod ) * (p%mod) % mod;
ans %= mod;
}
// ans %= mod ; ans += mod ;
ans %= mod;
ans = ans * inv( (r2-l2+1)%mod * ( (r1-l1+1)%mod ) % mod) % mod;
ans %= mod;
printf("%lld\n",ans);
}
}