参考题目:AcWing 1081. 度的数量
题意
求在给定区间内,满足以B进制表示的数中,恰好有K位是1的数的个数。
分析
f
数组:存放预处理出的组合数。
f[i][j]
,表示从i
个数中选出j
个数的个数。(组合数)
dp
函数:求出0 ~ n
中满足条件的数的个数,前缀和的思想。
-
num
数组:存放n
的每一位数字。 -
res
:dp
函数的结果。 -
last
:已经放1的位的个数。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 35;
int K, B;
int f[N][N]; // 组合数。
void init()
{
for (int i = 0; i < N; i ++ )
{
for (int j = 0; j <= i; j ++ )
{
if (!j) f[i][j] = 1;
else f[i][j] = f[i - 1][j] + f[i - 1][j - 1];
}
}
}
int dp(int n)
{
// 边界条件特判。
if (!n) return n;
// 将n转换为B进制。
vector<int> num;
while (n) num.push_back(n % B), n /= B;
int res = 0;
int last = 0;
for (int i = num.size() - 1; i >= 0; i -- )
{
int x = num[i];
// 左边的情况满足 0 ~ x - 1。
if (x)
{
// 不选1的情况。
res += f[i][K - last];
// 选1的情况,因为是0 ~ x - 1,所以x > 1才能选1。
if (x > 1)
{
if (K - last - 1 >= 0) res += f[i][K - last - 1];
// 如果x > 1,那么不存在右边的情况。
break;
}
else // x = 1的情况
{
last ++ ; // 选1的位数需要加一。
if (last > K) break; // 如果放的1多于限制条件的情况。
}
}
// 如果n是符合条件的数的情况。
if (!i && K == last) res ++ ;
}
return res;
}
int main()
{
init();
int l, r;
cin >> l >> r >> K >> B;
cout << dp(r) - dp(l - 1) << endl;
return 0;
}
参考资料:
https://www.acwing.com/solution/content/34003/