题目链接:https://vjudge.net/contest/372594#problem/E
题意:给定a和b,找出a到b之间所有数的二进制表示情况下1的总数目。
解题思路:
①预处理
dp[i]保存二进制情况下,i个数位全为1,所包含的1的总数,即小于等于1111…的所有数的包含1的数目总和
void init()
{
//dp[i]表示只有第i位有1时候所包含的所有情况(即小于等于11111....所含有1的个数)
dp[0]=0; c[0]=1;
for(int i=1;i<=60;i++){
dp[i]=2*dp[i-1]+c[i-1];
c[i]=c[i-1]*2;
}
}
②数位dp主体部分:
ll solve(ll x)
{
int num[64]={0};
int len=0;
ll res=0;
int cnt=0;
while(x){
num[len++]=x%2;
x/=2;
}
for(int i=len-1;i>=0;i--){
if(num[i]==1){
res+=dp[i]+cnt*c[i];
cnt++;
}
}
res+=cnt; //cnt为x本身含有的1的数目
return res;
}
以
100010010为例
可以把它分成三个部分
100000000和100010000和100010010
第一部分包含的1数目为dp[8]+0c[8]
因为第一部分为小于等于100000000的二进制数的1的数目和,不包含第二部分
第二部分包含1的数目和为dp[4]+1c[4],其中1*c[4]为第9位的1出现的次数。
第三部分同理。
最后要加上原数x中的1的数量cnt
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
ll dp[64];
ll c[64];
ll a,b;
void init()
{
//dp[i]表示只有第i位有1时候所包含的所有情况(即小于等于10000....所含有1的个数)
dp[0]=0; c[0]=1;
for(int i=1;i<=60;i++){
dp[i]=2*dp[i-1]+c[i-1];
c[i]=c[i-1]*2;
}
}
ll solve(ll x)
{
int num[64]={0};
int len=0;
ll res=0;
int cnt=0;
while(x){
num[len++]=x%2;
x/=2;
}
for(int i=len-1;i>=0;i--){
if(num[i]==1){
res+=dp[i]+cnt*c[i];
cnt++;
}
}
res+=cnt; //cnt为x本身含有的1的数目
return res;
}
int main()
{
init();
while(scanf("%lld%lld",&a,&b)!=EOF){
ll ans1,ans2;
ans1=solve(a-1);
ans2=solve(b);
printf("%lld\n",ans2-ans1);
}
return 0;
}