COJ1160[一本通 5.3 例 1」Amount of Degrees

 

试题描述

求给定区间[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

输入
第一行包含两个整数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;
}

  

posted @ 2018-12-05 17:23  niolle  阅读(222)  评论(0编辑  收藏  举报