Gym - 101982D Count The Bits

D. Count The Bits

数位DP

题意

给出 k k k b b b,计算 [ 0 , 2 b − 1 ] [0, 2^b-1] [0,2b1] 中所有 k k k 的倍数中有几个二进制 1 1 1

题解

数位DP。
一个数加上 2 i − 1 2^i-1 2i1 相当于在其二进制的第 i i i 位添上 1 1 1
记录下 [ 0 , 2 i − 1 ] [0,2^i-1] [0,2i1](i = 1,2,3……,b-1,b)的数转换为二进制后1的个数。 i i i 每增大 1 1 1 相当于 [ 0 , 2 i − 1 − 1 ] [0,2^{i - 1}-1] [0,2i11] 中的每个数加上 2 i − 1 2^{i-1} 2i1 再加上原来的 [ 0 , 2 i − 1 − 1 ] [0,2^{i - 1}-1] [0,2i11]
因为加上 2 i − 1 2^{i-1} 2i1 后余数会变。所以按照% k 所得的余数分类。

以k = 3 , b = 28 为例;
[ 0 , 2 28 − 1 ] [0,2^{28}-1] [0,2281]数划分为
[ 0 , 2 0 − 1 ] [0,2^0-1] [0,201] , [ 0 , 2 1 − 1 ] [0,2^1-1] [0,211] , [ 0 , 2 2 − 1 ] [0,2^2-1] [0,221] , [ 0 , 2 3 − 1 ] [0,2^3-1] [0,231]……( [ 0 , 2 i − 1 ] [0,2^i-1] [0,2i1]

建立一个f[130][1010]的数组,横坐标表示 i i i,纵坐标 j j j 表示 % k k k 的余数,里面存 x x x 的个数( x ϵ [ 0 , 2 i − 1 ] x\epsilon [0,2^i-1] xϵ[0,2i1], x x x% k k k == j j j)。

i i i = 0 时,只存在一个数 0 0 0,那么f[0][0] = 1;

i i i = 1 时候, [ 1 , 1 ] [1,1] [1,1]可以用 [ 0 , 0 ] [0,0] [0,0] + 2 0 2^0 20 表示。那么原来的 0+1=1,% k k k 的余数就变成了1所以,f[1][1] = f[0][0] + f[0][1] = 1;

i i i = 2 时候, [ 2 , 3 ] [2,3] [2,3]可以用 [ 0 , 1 ] [0,1] [0,1] + 2 1 2^1 21 表示。那么原来的 0+2=2,% k k k 的余数就变成了2所以,f[2][2] = f[1][2] + f[1][0] = 1 ;原来的1+2=3 , % k k k 的余数就变成了0,所以f[2][0] = f[1][1] + f[1][0] = 2; f[2][1] 还是原来的 f[1][1] = 1;

概括一下:f[i][j] = f[i - 1][j] + f[i-1][x] ( (x + 2 i − 1 2^{i-1} 2i1) % k == j )

建立一个g[130][1010]的数组,横坐标表示 i i i,纵坐标 j j j 表示 % k k k 的余数,里面存 所有 x x x 转化为二进制后1的个数的总和 ( x ϵ [ 0 , 2 i − 1 ] x\epsilon [0,2^i-1] xϵ[0,2i1] x x x% k k k == 纵坐标)。

g[i][j] = g[i-1][j] + g[i-1][x] + f[i-1][x] ( (x + 2 i − 1 2^{i-1} 2i1) % k == j )

g[i-1][j] 为原来就有的1数量。
纵坐标 为 x x x 的数 + 2 i − 1 2^{i-1} 2i1 后就变成了 %k == j 的数。
x x x 的数 + 2 i − 1 2^{i-1} 2i1 相当于在它们二进制的第 i i i 位 添一个1,在 [ 0 , 2 i − 1 − 1 ] [0,2^{i-1}-1] [0,2i11]里有 f[i-1][j]个数,则在 [ 0 , 2 i − 1 ] [0,2^i-1] [0,2i1] 要多添加 f[i-1][j] 个1,然后再加上g[i-1][x]原来就有的1的数量。

所以g[i][j] = g[i-1][j] + g[i-1][x] + f[i-1][x] ( (x + 2 i − 1 2^{i-1} 2i1) % k == j )

上代码

代码

#include<iostream>
using namespace std;

long long  f[130][1010] = {},g[130][1010] = {};

int main()
{
	const long long mod = 1e9+9;
	long long a = 1,k,b;
	cin>>k>>b;
	f[0][0] = 1;
	for (int i = 1;i<=b;i++)
	{
		for (int j = 0;j<=k-1;j++)
		{
			f[i][j] += f[i-1][j];
            f[i][(j+a)%k] += f[i-1][j];
            g[i][j] += g[i-1][j];
            g[i][(j+a)%k] += g[i-1][j] + f[i-1][j];
		}
		for (int j = 0; j < k; ++j) f[i][j] %= mod, g[i][j] %= mod;
		a = (a * 2) % k;
	}
	cout<<g[b][0];
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值