题解
考虑如何计算 f[l][r] f [ l ] [ r ] 。
很显然,可以分别计算 l,r l , r 的二进制中1的个数,然后减去(最大公共前缀的1的个数)*2。
考虑如何统计每一对 l,r l , r 的最大前缀中1的个数,可以用数位DP。
记 f[i][j][1/0][1/0] f [ i ] [ j ] [ 1 / 0 ] [ 1 / 0 ] 表示前 i i 位,最大公共前缀中1的个数为, l l 是/否等于, r r 是/否等于的方案数。这样,所有数对的最大公共前缀中1的个数 t t 显然就是,其中 len l e n 表示n的位数。
在转移的时候注意 l l 不能超过, r r 不能超过即可。
要计算所有数对中二进制中1的个数也可以用类似的方式。
最后, ∑nl=1∑nr=lf(l,r)=n∗∑二进制中1的个数−t ∑ l = 1 n ∑ r = l n f ( l , r ) = n ∗ ∑ 二 进 制 中 1 的 个 数 − t
时间复杂度: O(T∗log2n) O ( T ∗ l o g 2 n )
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define tt 1000000007
#define maxn 66
using namespace std;
inline char nc(){
static char buf[100000],*i=buf,*j=buf;
return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline LL _read(){
char ch=nc();LL sum=0;
while(!(ch>='0'&&ch<='9'))ch=nc();
while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
return sum;
}
int T,a[maxn];
LL n,nn,f[maxn][maxn][2][2],g[maxn][maxn][2];//f[i][j][1/0][1/0]:前i位,最长公共前缀中有j个1,L是否等于R,R是否等于n的方案数
//g[i][j][1/0]:前i位,有j个1,是否等于n的方案数
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
T=_read();
while(T--){
n=nn=_read();memset(f,0,sizeof(f));memset(g,0,sizeof(g));
int len=0;f[0][0][1][1]=1;g[0][0][1]=1;
while(nn)a[++len]=nn%2,nn/=2;
for(int i=1;i<=len>>1;i++)swap(a[i],a[len-i+1]);
for(int i=0;i<len;i++)
for(int j=0;j<=i;j++)
if(a[i+1]==1){
(f[i+1][j+1][1][1]+=f[i][j][1][1])%=tt,(f[i+1][j][0][1]+=f[i][j][1][1])%=tt,(f[i+1][j][1][0]+=f[i][j][1][1])%=tt;
(f[i+1][j+1][1][0]+=f[i][j][1][0])%=tt,(f[i+1][j][1][0]+=f[i][j][1][0])%=tt,(f[i+1][j][0][0]+=f[i][j][1][0])%=tt;
(f[i+1][j][0][1]+=f[i][j][0][1]*2%tt)%=tt,(f[i+1][j][0][0]+=f[i][j][0][1]*2%tt)%=tt;
(f[i+1][j][0][0]+=f[i][j][0][0]*4%tt)%=tt;
}else{
(f[i+1][j][1][1]+=f[i][j][1][1])%=tt;
(f[i+1][j+1][1][0]+=f[i][j][1][0])%=tt,(f[i+1][j][1][0]+=f[i][j][1][0])%=tt,(f[i+1][j][0][0]+=f[i][j][1][0])%=tt;
(f[i+1][j][0][1]+=f[i][j][0][1]*2%tt)%=tt;
(f[i+1][j][0][0]+=f[i][j][0][0]*4%tt)%=tt;
}
for(int i=0;i<len;i++)
for(int j=0;j<=i;j++)
if(a[i+1]==1){
(g[i+1][j+1][1]+=g[i][j][1])%=tt,(g[i+1][j][0]+=g[i][j][1])%=tt;
(g[i+1][j+1][0]+=g[i][j][0])%=tt,(g[i+1][j][0]+=g[i][j][0])%=tt;
}else{
(g[i+1][j][1]+=g[i][j][1])%=tt;
(g[i+1][j+1][0]+=g[i][j][0])%=tt,(g[i+1][j][0]+=g[i][j][0])%=tt;
}
LL ans=0,sum=0;
//for(int j=1;j<=len;j++)printf("%d %d %d %d\n",f[len][j][0][0],f[len][j][0][1],f[len][j][1][0],f[len][j][1][1]);
for(int i=1;i<=len;i++)(ans+=(f[len][i][0][1]+f[len][i][0][0])%tt*i%tt)%=tt;
for(int i=1;i<=len;i++)(sum+=(g[len][i][0]+g[len][i][1])*i%tt)%=tt;
printf("%lld\n",((sum*(n%tt)%tt-ans*2%tt)%tt+tt)%tt);
}
return 0;
}