ural 1057 Amount of degrees 【数位dp】
题意:求(x--y)区间转化为 c 进制 1 的个数为 k 的数的出现次数。
分析:发现其满足区间减法,所以能够求直接求0---x 的转化为 c 进制中 1 的个数为k的数的出现次数。
首先用一个数组f【i】【j】:表示前 i 位中有 j 位为 1 的个数。
能够通过方程 f【i】【j】 = f【i-1】【j】 + f【i-1】【j-1】来预处理出来。
对于要求的答案,我们能够借助树来求。
假如13 。2进制,有3个1 。转化为2进制 1101
能够借助于一个二进制的表示的树来求。
AC代码:
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> #include <map> #include <string> #include <iostream> #include <cmath> using namespace std; #define Del(a,b) memset(a,b,sizeof(a)) typedef long long LL; const double esp = 1e-10; const int N = 50; int f[N][N]; //f[i][j] 前i个中选j个1的个数 void isit() { 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 c) { vector<int> v; while(x) { v.push_back(x%c); x/=c; } int cnt = 0,ans = 0; for(int i=v.size()-1;i>=0;i--) { if(v[i]==1) //为1,则依次求解 { ans+=f[i][k-cnt]; cnt++; if(cnt==k) break; } else if(v[i]>1) //假如大于1的话。相当于全部的位有能够为1,所以直接求解跳出 { ans += f[i+1][k-cnt]; break; } } if(cnt==k) ans++; return ans; } int main() { isit(); int x,y,k,c; while(~scanf("%d%d%d%d",&x,&y,&k,&c)) { printf("%d\n",solve(y,k,c)-solve(x-1,k,c)); } return 0; }