AcWing 1081 度的数量

题目描述:

求给定区间 [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

输入格式

第一行包含两个整数 X 和 Y,接下来两行包含整数 K 和 B。

输出格式

只包含一个整数,表示满足条件的数的个数。

数据范围

1≤X≤Y≤2^31−1
1≤K≤20
2≤B≤10

输入样例:

15 20
2
2

输出样例:

3

分析:

题目意思是给定一个区间[X,Y],求这个区间中有多少个数满足条件,即这个数的B进制表示中恰好由K个1,并且剩下的位置上的数都是0。首先,数位DP常用的技巧有:求区间[l,r]中满足条件数的个数,只需要求0到r中满足条件数的个数s[r]以及0到l-1中满足条件数的个数s[l-1],所求数的个数等于s[r] - s[l - 1],这个技巧类似于前缀和。也就是说,本题中我们只需要求0到n中有多少个数满足条件即可。

设n的B进制表示为an,an-1,...,ai,...,a1,a0。我们可以从高位向低位遍历这个B进制数。

当ai = 0时:这个位置只能放0,所以可以跳过;

当ai > 0时:

如果ai > 1,那么第i位可以放0也可以放1,并且后面的位置不管怎么取都不会超过n了,设last为已经放置1的个数,则在ai处放0时,只需要在后面的位置选K - last个放1即可,即一共C(i,K-last)种情况。在ai处放1时,后面i位还需要放置K - last - 1个1,一共C(i,K-last-1)种放法。

如果ai = 1,这个位置放0是可以的,也就是需要在后面的位置继续放K - last个1,也可以放1,但是后面的位置就不能随便放数了,因为可能超过n,所以放1就暂且先将last++,继续考察下一位。

边界情况:某时刻last超过了K,就可以退出枚举的循环了。如果n恰好是K个1和剩下的0组成的,则枚举到最后一位时,方案数应该加1。

本题涉及到组合数,可以先预处理出来需要的组合数,用递推式C(n,m) = C(n-1,m-1) + C(n-1,m)即可,表示第n个物品选与不选的两种情况。

#include <iostream>
#include <vector>
using namespace std;
const int N = 35;
int C[N][N],K,B;
void init(){
    for(int i = 0;i < N;i++){
        for(int j = 0;j <= i;j++){
            if(!j)  C[i][j] = 1;
            else    C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
        }
    }
}
int get(int n){
    if(!n)  return 0;
    vector<int> num;
    while(n)    num.push_back(n % B),n /= B;
    int res = 0,last = 0;
    for(int i = num.size() - 1;i >=0;i--){
        int x = num[i];
        if(x){
            res += C[i][K - last];
            if(x > 1){
                res += C[i][K - last - 1];
                break;
            }   
            else{
                last++;
                if(last > K)    break;
            }
        }
        if(!i && last == K) res++;
    }
    return res;
}
int main(){
    int x,y;
    cin>>x>>y>>K>>B;
    init();
    cout<<get(y) - get(x - 1)<<endl;
    return 0;
}
发布了311 篇原创文章 · 获赞 31 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_30277239/article/details/104468664