题目:
思路:
首先对题目意思进行分析:恰好等于 K 个互不相等的 B 的整数次幂之和,这句话的意思是说对于某一个B进制数x说,x只有k位上只有1,其他位都为0。然后让你求区间[X,Y]的满足条件的数字有多少,这里我们可以用区间[1,Y]-[1,X-1]来更加容易的得到答案。
那么我们怎么获得前n个数的满足条件的数呢,首先将B进制数x中的 每一位用a表示为anan-1an-2an-3…a0,然后从第一位an开始,把它分为两部分0-an-1和an,所有的决策都可以由第一部分和第二部分的和组成(图片画横线的部分)。
当我们选第一部分0-an-1的时候,那么第an-1位以及以后的数字我们可以任意选取,(不会超过x),但是因为题目要求只能选0或者1,那么先取0的话就是n个数(因为最后一个数的下标为a0)中选出k-last(当前已经选了多少个1)个1的结果,如果取1的话那么就是n个数中选出k-last个1的结果,这部分结果可以用组合数来求得(根据组合数公式C(a,b)=C(a-1,b)+C(a-1,b-1)来获得),但是取1有个前提条件,那么就是an必须大于1(因为如果an等于1的话,那么他是会分到第二部分的)。
这样第一部分就选完了,那么对于第二部分an的时候,如果an不为1,那么根据题意不能选择除了0或者1之外的任意数字,那么直接break退出即可;否则如果为1,那么last就要++(接下来需要的1少一个),然后继续往下走每次计算左边的决策,然后走到最后一位的时候a0如果选0,那么就可以根据判断条件[last=k]是否成立,如果成立结果res++即可;如果选1,那么也会last++,然后因为走到了最后一位,后面没有可以再进行的决策,那么还是根据[last=k]是否成立来使res++。
大体思路就是这样子的,具体细节可以看看代码:
/**
* ┏┓ ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃ ┃
* ┃ ━ ┃ ++ + + +
* ████━████+
* ◥██◤ ◥██◤ +
* ┃ ┻ ┃
* ┃ ┃ + +
* ┗━┓ ┏━┛
* ┃ ┃ + + + +Code is far away from
* ┃ ┃ + bug with the animal protecting
* ┃ ┗━━━┓ 神兽保佑,代码无bug
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛ + + + +
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛+ + + +
*/
#include<cstdio>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include<vector>
#include<queue>
#include<map>
#define sc_int(x) scanf("%d", &x)
#define sc_ll(x) scanf("%lld", &x)
#define pr_ll(x) printf("%lld", x)
#define pr_ll_n(x) printf("%lld\n", x)
#define pr_int_n(x) printf("%d\n", x)
#define ll long long
using namespace std;
const int N=1000000+100;
int x,y,k,b;
int s[40][40];//因为如果是二进制位可能会有30位多开了点
void cal()//用于计算组合数
{
for(int i =0;i<=40;i++)
for(int j =0;j<=i;j++)
if(j==0)s[i][j]=1;//i个数中不选也有一种方案
else s[i][j]=s[i-1][j]+s[i-1][j-1];
}
int dp(int n)
{
if(n==0)return 0;//特判,0的时候没有任何一种方案
vector<int>cnt;//用来存储进制转换后的数
while(n)cnt.push_back(n%b),n/=b;
int res=0;//结果
int last=0;//到当前情况已经选了多少个1
for(int i =cnt.size()-1;i>=0;i--)//从最高位开始运算
{
int x=cnt[i];
if(x)//如果x大于等于1
{
res+=s[i][k-last];//i个数中选k-last个1
if(x>1)
{
if(k-last-1>=0) res+=s[i][k-last-1];//选1的情况就少了一个1可以选择,
//到此左半部分计算完毕
break;//因为x大于1的时候右半部分不能得到1,直接退出即可
}
else{//x为1 的情况
last++;//已经选了的1增加
if(last>k)break;//如果已经选了1的数量大于k那么也可以直接退出
}
}
if(i==0&&k==last)res++;//走到了a0的情况判断一下是否可以构成一种方案
}
return res;
}
int main()
{
cal();
cin>>x>>y>>k>>b;
cout<<dp(y)-dp(x-1)<<endl;
return 0;
}
看y总的大概思路就是把这些数按树形结构分成两部分考虑,每一部分的答案根据上一部分获得,反正自己也不是很会,只能尽量写了那么清楚了qwq