UVA 12670. Counting ones(数位DP)

题目链接: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]+1
c[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Buyi.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值