URAL 1057 Amount of Degrees
数位DP,题意转换一下就是说,其B进制表示的各位数字都只能是0和1。
求区间L....R之间的符合要求数字有几个,可以使用容斥原理。
计算小于X的符合要求的数有几个的时候,我们需要先找到比X小的最大的符合要求的数字,然后以这个数字往下推导,和之前做的数位DP方法一样了。
dp[i][j][k]表示的是i位,首位数字为j,有k个1的数的个数是多少。
递推式很容易写出来:
dp[i][0][k] += dp[i - 1][0][k];
dp[i][0][k] += dp[i - 1][1][k];
dp[i][1][k] += dp[i - 1][0][k - 1];
dp[i][1][k] += dp[i - 1][1][k - 1];
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int dp[35][5][30]; int B,K; int L,R; int p[35]; int tot; void init() { memset(dp,0,sizeof dp); dp[1][0][0]=1; dp[1][1][1]=1; for(int i=2; i<=32; i++) { for(int j=0; j<=1; j++) { for(int k=0; k<=20; k++) { if(j==0) { dp[i][0][k]+=dp[i-1][0][k]; dp[i][0][k]+=dp[i-1][1][k]; } else if(j==1) { if(k-1>=0) { dp[i][1][k]+=dp[i-1][0][k-1]; dp[i][1][k]+=dp[i-1][1][k-1]; } } } } } } int f(int x) { tot=1; while(x) { p[tot++]=x%B; x=x/B; } tot--; //找到第一个比x小,并且能表示的数字 for(int i=tot; i>=1; i--) { if(p[i]!=1&&p[i]!=0) { for(int j=i; j>=1; j--) p[j]=1; break; } } int res=0; for(int i=1; i<tot; i++) res=res+dp[i][1][K]; int sum=1;//高位确定有sum个1 for(int i=tot-1; i>=1; i--) { if(p[i]==1) { res=res+dp[i][0][K-sum]; sum++; } } sum=0; for(int i=1; i<=tot; i++) if(p[i]==1) sum++; if(sum==K) res++; return res; } int main() { init(); while(~scanf("%d%d",&L,&R)) { scanf("%d%d",&K,&B); printf("%d\n",f(R)-f(L-1)); } return 0; }