题目描述
求给定区间 [X,Y] 中满足下列条件的整数个数:这个数恰好等于 K 个互不相等的 B 的整数次幂之和。
例如,设 X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意:
$17=2^4+2^0$
$18=2^4+2^1$
$20=2^4+2^2$
样例
输入
15 20
2
2
输出
3
数位DP
K个互不相同的B的整数次幂,等价于求数字num在B进制表示下是否是由K个1且其他位全是0组成,可用数位Dp来做。
对于数字num,存放其每一位上的数字(B进制表示下),从高位向低位枚举,假设枚举到第i位,第i位上的数字是x,那么分以下几种情况讨论:
- 1.x == 0
那么第i
位只能是0
,后面数位上现在都不能确定,只能继续向后看. - 2.x == 1
这里第i
位可以分成两种情况:- 1.第
i
位放0
,那么后面的数位上可以放k-last个1
,res += f[i][k-last]; - 2.第
i
位放1
,那么后面数位上的情况不能用组合数计算,因为要保证答案中的数字比原数字要小
- 1.第
- 3.x > 1
同样第i
位分成两种情况- 1.第
i
位放0
,那么后面的数位上可以放k-last个1
,res += f[i][k-last]; - 2.第
i
位放1
,那么后面的数位上可以放k-last-1个1
,res += f[i][k-last-1];
- 1.第
C++ 代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 34;
int f[N][N]; //组合数
int k,b;
int l,r;
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){
int res = 0, last = 0;
if(!n) return res;
vector<int> a;
while(n) a.push_back(n%b),n/=b;
int len = a.size()-1;
for(int i = len; i>=0; --i){
int x = a[i];
if(x){
res += f[i][k-last]; //第i位放0,后i-1位放k-last个1(0~i-1位,所以第i位后面有i位)
if(x>1){
//第i位放1,后i-1位放k-last-1个1
if(k-last-1>=0) res += f[i][k-last-1];
break; //这一位已经不合法了,所以上一步结束后直接break
}else{
//如果x为1,考虑到组成的数必须比原数字小,所以这里不能用组合数计算
last++;
if(last>k) break; //1如果放完了,直接break
}
}
if(!i && last == k) res ++; //树的最右侧的分支
}
return res;
}
int main()
{
init(); //预处理出组合数
cin>>l>>r>>k>>b;
cout<<dp(r) - dp(l-1);
return 0;
}
我靠,思考了很久很久,也看了很多题解和评论,依旧找不到我疑惑的点,但是在这里我终于找到了“保证答案中的数字比原数字要小”,感谢感谢
写的真好, 明白了
二进制填1我可以理解 为什么其他进制也要填1呢
可以看看下面的评论
好滴好滴
这里没太看懂,大佬能解释一下吗,既然第i位已经是x了而x又等于1,那为什么还要分情况讨论呢
举个例子,二进制下
1011
,假设K为2,先枚举最高位是1:1100
,这就比上界还要大了,所以我们用last先记录一下。
这样统计答案才能不重不漏~
如果没理解或者我哪里说错了,私信hh~
谢谢大佬,看懂了
请问 为什么K个互不相同的B的整数次幂,等价于求数字num在B进制表示下是否是由K个1且其他位全是0组成的呢
K个互不相同的B的整数次幂之和,假设当前一个数在B进制表示下是 a0bc0d,即
a*B^5 + b*B^3+c*B^2+d*B^0
,当a,b,c,d都是1的时候,才满足题意。所以这个等价就在于这几个整数次幂的系数
为1,所以可以等价。如果我没说清楚,您可以私信我,再交流~
懂啦懂啦 谢谢您
懂了,谢谢您, vector 里面存的应该就是 枚举的系数那些,然后对这些系数进行讨论吧
respect!!!
if(!i && last == k) res ++;
为什么此时要res++
呢,按道理此时这一位固定了i == 0
时,不管x
是0
还是1
,组合数f[0][k-last]
都是0
,特判加上答案即可举个例子, 如果
n
是13(1101)
, 那么枚举到最后一位1
时,res += f[0][1]
是没变的如果
n
是14(1110)
,那么枚举到最后一位0
时,res
加上的就是14(1110)
本身,这当然是一个合法方案~如果没理解,可以继续交流。
是不是说如果
!i&&last==k
成立的话,就还有一个合法方案对的~
懂了
是
i==0
时,不管x
时0
还是1
,res
都要++
的意思吗?应该是如果x是大于0,它就不会执行这条代码,而是走x>0那个判断