思路
这是一道数位dp问题,对于数位dp问题关键就在于分类讨论。
首先我们把数字 n 对于B进制来进行分解 ,将每一位上的数字存入一个数组中,然后从高位往低位去讨论,
首先 对于第 i 位数字 x 有三种情况
- x = 0 :则 i 位上只能取 0 ,所以直接讨论 i-1 位就可以了
- x = 1 :则 i 位上取 0 的时候,后面i-1位都可以随意取值 ,取 1 的时候,后面i- 1位要再小于题目的数的前提下取值,并且能取 k-last-1 个 1 ,
- x > 1 :则 i 位上 可以取 1 ,0 ,并且后面 i-1 位可以随便取。
这里 i-1位随便取 和 对于i-1 位讨论的区别,就体现在前一个直接用组合数 f[i-1][k-last]就可以,后面则需要再进入循环去讨论。
最后对于最后一位进行单独讨论,如果对于 最后一位时 ,所有的k个1都已经取好了,也就是k==last了,才做res++。
代码
#include <cstring>
#include <iostream>
#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-1]+f[i-1][j];
}
}
}
int dp(int n){
if (!n) return 0;//如果n==0,那么就直接放回0
vector<int> v;
while (n) v.push_back(n % B), n /= B;
int res=0;
int last=0;//表示已经取了多少个1
for(int i=v.size()-1;i>=0;i--){//从最高位对每一位数讨论
int x=v[i];
if(x){
res += f[i][K - last];//加上第i位取0的时候的组合数,也就是对于后面i位取k-last个1的数量
if (x > 1)//如果x>1,就可以直接用组合数表示出来,不用进行讨论,也就是i位取1的时候,后面i位随便取k-last-1个1
{
if (K - last - 1 >= 0) res += f[i][K - last - 1];
break;
}
else//如果x==1,那么i位取1的时候,还要进行讨论,后面i位不能随便取,也就不是组合数
{
last ++ ;
if (last > K) break;
}
}
if (!i && last == K) res ++ ;// 对于最后一位来特殊来考虑
}
return res;
}
int main()
{
init();
int l, r;
cin >> l >> r >> K >> B;
cout << dp(r) - dp(l - 1) << endl;
return 0;
}