题目描述
设 是个 进制数,并满足以下条件:
- 至少是个 位的 进制数。
- 作为 进制数,除最后一位外, 的每一位严格小于它右边相邻的那一位。
- 将 转换为二进制数 后,则 的总位数不超过 。
在这里,正整数 是事先给定的。
问:满足上述条件的不同的 共有多少个?
我们再从另一角度作些解释:设 是长度为 的 字符串(即字符串 由 个 或 组成), 对应于上述条件三中的 。将 从右起划分为若干个长度为 的段,每段对应一位 进制的数,如果 至少可分成 段,则 所对应的二进制数又可以转换为上述的 进制数 。
例:设 。则 是个八进制数( )。由于 ,长度为 的 字符串按 位一段分,可分为 段(即 ,左边第一段只有一个二进制位),则满足条件的八进制数有:
位数:
高位为
:
个(即
),
高位为
:
个,
,
高位为
:
个(即
)。
共
个。
位数:
高位只能是
,
第
位为
:
个(即
),
第
位为
:
个,
,
第
位为
:
个(即
)。
共
个。
所以,满足要求的 共有 个。
输入格式
一行两个正整数 用一个空格隔开:
输出格式
一行一个个正整数,为所求的计算结果。
即满足条件的不同的
的个数(用十进制数表示),要求不得有前导零,各数字之间不得插入数字以外的其他字符(例如空格、换行符、逗号等)。
(提示:作为结果的正整数可能很大,但不会超过 位)
输入输出样例
输入 #1 复制
3 7
输出 #1 复制
36
说明/提示
【数据范围】
提高组 第四题
思路
首先,可以看出来这是一道组合数学的题目。
那么,我们一起来推一推式子。
以样例为例:
3 7
因为这一个 的数转换成谔进制是不能超过 位的,而
所以,这样的一位就要可以分成二进制的 位。
例如 进制的 。
一位一位转化
其实这里的一位一位的就是相当于 进制下的,因为每一位都不超过 。
所以最后的结果就是这几个谔进制的数写在一起: 加了个间隔看得清楚 。
然后,再回到样例,一个 进制的数转换成谔进制最多只能有 位。
我们刚才知道了, 的数每一位就可以转换成 位的谔进制,那么 位,就可以有 位,然后剩下 ,也就是说这个 进制的最高位转换成谔进制最多只能是 ,所以这个 进制的数最多只能有 位,最高位不能超过 。
那如果样例是:
3 8
首先,两位是肯定可以有的,然后余下 位的谔进制,那这个 位谔进制最多就是 ,也就是 。
这样,如果 余下的是 ,那么最高位就不能超过
这样就总结出了:这个 进制的数最多只能有 ,这是一个向上取整的符号,第 位不能超过 。
然后就是一个十分简单的组合数。
让我们模拟一遍(还是样例):
- 求出位数 ,第 不能超过 。
- 枚举位数:两位,从 到 中选 个,一共有
- 继续枚举位数 直到
- 枚举最高位的数:1,后面的数可选 到 ,一共有
- 继续枚举最高位的数 直到
- 输出答案
好了,就是这么一个过程,题目中也很清楚,要高精度,高精度的模板可以从高精度模板–zhengjun拿(我就是用这个模板过的)
最后说一句,高精度的除法比较慢,我一开始算 时用了这个公式: ,结果就 掉了。
反正要算 , 又不大,直接预处理出来放在数组里面用就可以了,用递推公式(不难理解): 。需要我说吗?
我还是唠一下吧。就是看这第 个数要不要选,如果选了,剩下的可能就是 ,如果不选,就是 。
注意一下初始值:
所有的 都是 ,一个也不选就是一种情况。
好了,我太多嘴了
我的代码为了简洁,高精度模板自己去高精度模板–zhengjun拿(顺便点个赞),这里就省略掉了。
代码
#include<bits/stdc++.h>
using namespace std;
/*
高精度模板
*/
int k,w;
bign c[512][512];
bign ans;
int main(){
scanf("%d%d",&k,&w);
int t=(1<<k)-1,a=(1<<(w%k))-1;//谔进制就用位运算了
for(int i=0;i<=t;i++){
c[i][0]=1;//初始化
for(int j=1;j<=i;j++)
c[i][j]=c[i-1][j-1]+c[i-1][j];
}
for(int i=2;i<=w/k&&i<=t;i++)ans+=c[t][i];//注意一下边界2:i<=t
for(int i=1;i<=a&&t-i>=w/k;i++)ans+=c[t-i][w/k];//同样注意边界
cout<<ans;//我的模板支持输入输出流
return 0;
}