1. 程式人生 > >URAL 1057. Amount of Degrees (數位DP)

URAL 1057. Amount of Degrees (數位DP)

1057. Amount of Degrees

Time limit: 1.0 second
Memory limit: 64 MB
Create a code to determine the amount of integers, lying in the set [X;Y] and being a sum of exactlyK different integer degrees of B. Example. Let X=15, Y=20, K=2, B=2. By this example 3 numbers are the sum of exactly two integer degrees of number 2:
17 = 24+20,
18 = 24+21,
20 = 24+22.

Input

The first line of input contains integers X and Y, separated with a space (1 ≤ X ≤ Y ≤ 231−1). The next two lines contain integers K and B (1 ≤ K ≤ 20; 2 ≤ B ≤ 10).

Output

Output should contain a single integer — the amount of integers, lying between X and Y
, being a sum of exactly K different integer degrees of B.

Sample

input output
15 20
2
2
3
題目大意: 求給定區間[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. 1 ≤ X ≤ Y ≤ 2^31−1,1 ≤ K ≤ 20,  2 ≤ B ≤ 10。 

實質上:求[X,Y] 

區間轉化為 B 進位制 1 的個數為K 的數的出現次數。

題解:數位DP。

注意:只有係數為1的情況,所以每位只能是0,1,至於不相同的只要是取1 的時候不不相同,至於0是不變的。

大體思路: 所求的數為互不相等的冪之和,亦即其B進製表示的各位數字都只能是0和1。 因此,我們只需討論二進位制的情況,其他進位制都可以轉化為二進位制求解。 本題區間滿足區間減法,因此可以進一步簡化問題:令count[i..j]表示[i..j]區間內合法數的個數,則 count[i..j]=count[0..j]-count[0..i-1]。 換句話說,給定n,我們只需求出從0到n有多少個符合條件的數。 步驟: 首先預處理f。 f[i,j]代表i位二進位制數中恰好有j個1的數的個數。 f[i,j]=f[i-1,j]+f[i-1,j-1] 計算count[0..n] 像前幾題一樣,一位一位列舉,只需要多記錄後面需要的1的個數即可。 if digit[i] = 1 then ans = ans + f[i,need] need就是後面需要的1的個數。 AC程式碼:
#include <stdio.h>
#include <cstring>
#include <vector>
#include <algorithm>
#include <string.h>
using namespace std;
int digit[33];
typedef long long LL;
const int N = 50;
int f[N][N]; //f[i][j]表示前i箇中選 j 個 1的個數
void init()
{
    f[0][0] =  1;
    for(int i=1;i<33;i++)
	{
        f[i][0] = f[i-1][0];
        for(int j=1;j<=i;j++)
        {
        	f[i][j] = f[i-1][j] + f[i-1][j-1];
		 } 
            
    }
}
int solve(int x,int k,int B)
{
	int len=-1;
    //vector<int> v;
    while(x)
    {
    	digit[++len]=x%B; 
        //v.push_back(x%B);
        x/=B;
    }
    int cnt = 0,ans = 0;
    for(int i=len;i>=0;i--)
    {
        if(digit[i]==1) // 如果為 1,則依次求解
        {
            ans+=f[i][k-cnt]; //need 
            cnt++;
            if(cnt==k)
                break;
        }
        else if(digit[i]>1) //假如大於1的話,相當於所有的位可以為 1,所以直接求解跳出
        {
            ans += f[i+1][k-cnt];
            break;
        }
    }
    if(cnt==k)
        ans++;
    return ans;
}
int main()
{
    init();
    int x,y,k,B;
    while(~scanf("%d%d%d%d",&x,&y,&k,&B))
    {
        printf("%d\n",solve(y,k,B)-solve(x-1,k,B));
    }
    return 0;
}