试题描述
|
求给定区间[X,Y]中满足下列条件的整数个数:这个数恰好等于K个互不相等的B的整数次幂之和。例如,设X=15,Y=20,K=2,B=2,则有且仅有下列三个数满足题意: |
输入
|
第一行包含两个整数X和Y,接下来两行分别包含整数K和B。
|
输出
|
只包含一个整数,表示满足条件的数的个数。
|
输入示例
|
15 20
2 2 |
输出示例
|
3
|
其他说明
|
数据范围:对于全部数据,1 <= X <= Y <= 2^31−1,1 <= K <= 20,2 <= B <= 10。
|
思路:
这是一道奇怪的数位DP
第一步,我们要把这个输入的十进制转化成一个b进制数
首先我们想到2进制的情况,设dp[i][j]表示后i位有j个1
显然,dp[i][j]=dp[i-1][j]+dp[i-1][j-1]
然后查询时,我们只需要知道当前数字%2是否为0
将已经取了的数字1个数记为t
若为0,说明这一位无法进行操作
若为1,固定这一位是1,问题变成了求
(1)取(1<<i)这个1,sum=在a-(1<<i)中取k-t-1个1
(2)不取取(1<<i)这个1,sum=在a中不取(1<<i)取k-t个1
两个和起来即为答案
即sum=cnt(a-(1<<i),k-t-1)+dp[i-1][k-t]的和(前者没有说明好说的,后者下一层随便搞都不会超过(1<<i),所以直接套用dp即可)
如果最后发现k==t,直接退出递归
但是我们发现,如果k==t时我们最后一次直接退出返回值是0,而实际上应该是1
所以最后sum应加上1
推己及彼,举壹反叁,我们发现任何进制,对于这一位为0时都无法做运算
对于这一位为1时,和二进制的做法相同
B进制和二进制唯一的区别是当这一位大于0时,我们发现从这一位随便搞都无法超过x^i(x为这一位的数字)
所以此时只需要加上一个dp[i][k-t],然后退出循环即可
好了
上代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define rep(i,a,b) for(int i=a;i<=b;i++) #define dwn(i,a,b) for(int i=a;i>=b;i--) using namespace std; int dp[32][25],x,y,k,b,tot,p[32]; int cnt(int a){ tot=0;int sum=0,t=0; while(a){ p[++tot]=a%b; a/=b; } dwn(i,tot,1){ if(t==k) break; if(p[i]==1) sum+=dp[i-1][k-t],t++; else if(p[i]){sum+=dp[i][k-t]; break;} } if(t==k) sum++; return sum; } int main(){ scanf("%d%d%d%d",&x,&y,&k,&b); rep(i,0,31) dp[i][0]=1; rep(i,1,31) rep(j,1,k) dp[i][j]=dp[i-1][j]+dp[i-1][j-1]; printf("%d",cnt(y)-cnt(x-1)); return 0; }