DP - 數位DP - 度的數量
求給定區間 [X,Y] 中滿足下列條件的整數個數:這個數恰好等於 K 個互不相等的 B 的整數次冪之和。
例如,設 X=15,Y=20,K=2,B=2,則有且僅有下列三個數滿足題意:
17=24+20
18=24+21
20=24+22
輸入格式
第一行包含兩個整數 X 和 Y,接下來兩行包含整數 K 和 B。
輸出格式
只包含一個整數,表示滿足條件的數的個數。
數據範圍
1≤X≤Y≤231−1,
1≤K≤20,
2≤B≤10
輸入樣例:
15 20
2
2
輸出樣例:
3
分析:
借助前綴和的思想,先求1~N中滿足條件的數的個數Sn,再求區間[l,r]中的個數=Sr−Sl−1。
Count函數求1~N中滿足條件的數的個數,將N先轉化爲一個B進制數存入數組。
依據題意,要恰好表示成K個Bi次方的數之和,等價於B進制表示下有K位爲1,其餘爲0。
問題轉化在N的B進制表示下,選擇K個位置取1的所有取法的方案總數。
從高位到低位依次遍歷,
設N的B進制表示爲V=an−1an−2...a0,當前已經選取了last個′1′:
①、若ai>0:Ⅰ、我們可以將第i位取0,這樣ai−1...a0無論取何值均不會大於V。總方案數爲CiK−last,即在剩下i位中選擇K−last個位置取′1′。
Ⅱ、我們可以將第i位取1,若ai>1,ai−1...a0無論取何值均不會大於V。總方案數爲CiK−last−1,即在剩下i位中選擇K−last−1個位置取′1′。此時,所有合法方案已均被計算出來,退出循環。
若ai=1,後面的取法將在第Ⅰ步被計算出來,因此這裏不必重復計算,last++後直接看下一位。
②、若ai=0:後面的合法方案同樣會在①−Ⅰ中被計算出來,直接看下一位。
③、最後,若考慮到a0時已經取了K個1,說明V的第ai位都恰好是1,答案還要再加上等於V的方案。
代碼:
#include<iostream>
#include<vector>
using namespace std;
const int N=35;
int K,B;
int C[N][N];
void cal()
{
for(int i=0;i<N;i++)
for(int j=0;j<=i;j++)
if(!j) C[i][j]=1;
else C[i][j]=C[i-1][j]+C[i-1][j-1];
}
int dp(int n)
{
if(!n) return 0;
vector<int> V;
while(n) V.push_back(n%B),n/=B;
int res=0,last=0;
for(int i=V.size()-1;i>=0;i--)
{
int x=V[i];
if(x)
{
res+=C[i][K-last];
if(x>1)
{
if(K-last-1>=0) res+=C[i][K-last-1];
break;
}
else
{
last++;
if(last>K) break;
}
}
if(i==0 && last==K) res++;
}
return res;
}
int main()
{
cal();
int l,r;
cin>>l>>r>>K>>B;
cout<<dp(r)-dp(l-1)<<endl;
return 0;
}