1. 程式人生 > >COJ1160[一本通 5.3 例 1」Amount of Degrees

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;
}