题目链接:URAL1057


题意:区间【X,Y】内,有多少个数可以表示成K个不同的B的幂次方之和

如:K=2,B=2,3=2^0+2^1,5=2^2+2^0等


这个题是论文上的题:算法合集之《浅谈数位类统计问题》

其实归到数位DP不是很合适,因为数学推导推完了之后就是一个组合数的问题


详细分析见论文吧,不再赘述,说说我对代码的理解

#include<map>
#include<set>
#include<math.h>
#include<time.h>
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<stdio.h>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cstdlib>
using namespace std;

#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define ll rt<<1
#define rr rt<<1|1
#define LL long long
#define ULL unsigned long long
#define maxn 50
#define maxnum 1000050
#define eps 1e-6
#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)

int x,y,k,b,C[maxn][maxn];
int digit[maxn];

int calc(int x,int k,int b){
	int len=0,ans=0;
	while(x){
		digit[++len]=x%b;
		x/=b;
	}
	for(int i=len;i>=0;i--){
		if (digit[i]>1){
			//考虑这一位不取答案是C[i-1][K] 
			//考虑这一位取,答案是C[i-1][K-1]
			//所有情况考完了,可以退出循环了 
			ans+=C[i-1][k]+C[i-1][k-1];
			break;
		}
		else if (digit[i]==1){
			//考虑这一位不取的情况 
			ans+=C[i-1][k];
			k--;
		}
		if (k<0) return ans;
	}
	return ans;
}

int main(){
	//input;
	C[0][0]=1;
	for(int i=1;i<=40;i++){
		C[i][0]=C[i-1][0];
		for(int j=1;j<=i;j++)
			C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
	scanf("%d%d%d%d",&x,&y,&k,&b);
	cout<<calc(y+1,k,b)-calc(x-1+1,k,b)<<endl;
	return 0;
}