题意
求给定区间[X,Y]中满足下列条件的整数个数:这个数恰好等于K 个互不相等的B的整数次幂之和。
思路
第一道数位DP题,参考09年国家集训队论文《浅谈数位类统计问题》。
这类问题的第一步一般都先
把区间[X,Y]转化为区间[1,X]和[1,Y]。
【重要思想---数形结合、按位处理】
按照数的二进制位构建一个类似Trie树的一个二进制树,在树上进行位操作。树的每一层对应每一位。
我们先来看2进制。在这道题中,我们用f[i][j]表示高度为i含有j个1的树的个数(设叶子节点高度为0,且f[0][0] = 1),tot表示当前路径根节点上已经有的1的个数。对于一个数x,我们在它的二进制树上操作,遇到0左拐,遇到1右拐(进入右子树),并且计算它的左兄弟子树中有K-tot个1的个数,再将tot+1。如下图所示,红色为x的路径,蓝绿紫三个子树分别是三次右拐时计算的子树。
而处理大于2进制位的B进制位时,把它当作二进制位处理即可:我们先需要把每位的数值都转化为1或0。我们只需要向下找最大的符合条件的那个数,就是从左寻找第一个不是0或1的数,然后把右边的每位数字都改为1。构建好二进制数以后,按位DP即可。注意最后要是n也满足条件,也要加上去。
代码
[cpp]
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, m) for (int i = begin; i < begin+m; i ++)
using namespace std;
int f[35][35];
void init(){
int n = 31;
f[0][0] = 1;
REP(i, 1, n){
f[i][0] = f[i-1][0];
REP(j, 1, i){
f[i][j] = f[i-1][j] + f[i-1][j-1];
}
}
}
int change_to_bin(int x, int b){
vector v;
while(x){
v.push_back(x%b);
x /= b;
}
for (int i = (int)v.size()-1; i >= 0; i --){
if (v[i] > 1){
for (int j = i; j >= 0; j --){
v[j] = 1;
}
break;
}
}
int ans = 0;
for (int i = 0; i < (int)v.size(); i ++){
ans += v[i] * (1 << i);
}
return ans;
}
int cal(int x, int k){
int num = 0, tot = 0;
for (int i = 30; i >= 0; i --){
if (x & (1<
k) break;
x = x^(1<