洛谷 P1066 2^k进制数题解--zhengjun
题目描述
设 \(r\) 是个 \(2^k\) 进制数,并满足以下条件:
- \(r\) 至少是个 \(2\) 位的 \(2^k\) 进制数。
- 作为 \(2^k\) 进制数,除最后一位外,\(r\) 的每一位严格小于它右边相邻的那一位。
- 将 \(r\) 转换为二进制数 \(q\) 后,则 \(q\) 的总位数不超过 \(w\)。
在这里,正整数 \(k,w\) 是事先给定的。
问:满足上述条件的不同的 \(r\) 共有多少个?
我们再从另一角度作些解释:设 \(S\) 是长度为 \(w\) 的 \(01\) 字符串(即字符串 \(S\) 由 \(w\) 个 \(0\) 或 \(1\) 组成),\(S\) 对应于上述条件三中的 \(q\)。将 \(S\) 从右起划分为若干个长度为 \(k\) 的段,每段对应一位 \(2^k\) 进制的数,如果 \(S\) 至少可分成 \(2\) 段,则 \(S\) 所对应的二进制数又可以转换为上述的 \(2^k\) 进制数 \(r\)。
例:设 \(k=3,w=7\)。则 \(r\) 是个八进制数( \(2^3=8\))。由于 \(w=7\),长度为 \(7\) 的 \(01\) 字符串按 \(3\) 位一段分,可分为 \(3\) 段(即 \(1,3,3\),左边第一段只有一个二进制位),则满足条件的八进制数有:
\(2\) 位数:
高位为 \(1\):\(6\) 个(即\(12,13,14,15,16,17\)),
高位为 \(2\):\(5\) 个,
\(\cdots\),
高位为 \(6\):\(1\) 个(即 \(67\) )。
共 \(6+5+…+1=21\) 个。
\(3\) 位数:
高位只能是 \(1\),
第 \(2\) 位为 \(2\):\(5\) 个(即 \(123,124,125,126,127\)),
第 \(2\) 位为 \(3\):\(4\) 个,
\(\cdots\),
第 \(2\) 位为 \(6\):\(1\) 个(即 \(167\))。
共 \(5+4+…+1=15\) 个。
所以,满足要求的 \(r\) 共有 \(36\) 个。
输入格式
一行两个正整数 \(k,w\) 用一个空格隔开:
输出格式
一行一个个正整数,为所求的计算结果。
即满足条件的不同的 \(r\) 的个数(用十进制数表示),要求不得有前导零,各数字之间不得插入数字以外的其他字符(例如空格、换行符、逗号等)。
(提示:作为结果的正整数可能很大,但不会超过 \(200\) 位)
输入输出样例
输入 #1 复制
3 7
输出 #1 复制
36
说明/提示
【数据范围】
\(1\le k \le 9\)
\(1\le w \le 3\times 10^4\)
\(NOIP\ 2006\) 提高组 第四题
思路
首先,可以看出来这是一道组合数学的题目。
那么,我们一起来推一推式子。
以样例为例:
3 7
因为这一个\(2^3\)的数转换成谔进制是不能超过\(7\)位的,而\(2^3=8=1000_{(2)}\)
所以,这样的一位就要可以分成二进制的\(k\)位。
例如\(2^4\)进制的\(1032\)。
一位一位转化
\(1_{(16)}=0001_{(2)}\)
\(0_{(16)}=0000_{(2)}\)
\(3_{(16)}=0011_{(2)}\)
\(2_{(16)}=0010_{(2)}\)
其实这里的一位一位的就是相当于\(10\)进制下的,因为每一位都不超过\(2^k\)。
所以最后的结果就是这几个谔进制的数写在一起:\(0001,0000,0011,0010(\)加了个间隔看得清楚\()\)。
然后,再回到样例,一个\(2^3\)进制的数转换成谔进制最多只能有\(7\)位。
我们刚才知道了,\(2^k\)的数每一位就可以转换成\(k\)位的谔进制,那么\(7\)位,就可以有\(2\)位,然后剩下\(1\),也就是说这个\(2^k\)进制的最高位转换成谔进制最多只能是\(1\),所以这个\(2^k\)进制的数最多只能有\(3\)位,最高位不能超过\(1\)。
那如果样例是:
3 8
首先,两位是肯定可以有的,然后余下\(2\)位的谔进制,那这个\(2\)位谔进制最多就是\(11_{(2)}\),也就是\(3\)。
这样,如果\(w\bmod k\)余下的是\(x\),那么最高位就不能超过\(2^x-1\)
这样就总结出了:这个\(2^k\)进制的数最多只能有\(\lceil w\div k\rceil\),这是一个向上取整的符号,第\(\lfloor w\div k\rfloor+1\)位不能超过\(2^{w\bmod k}-1\)。
然后就是一个十分简单的组合数。
让我们模拟一遍(还是样例):
- 求出位数\(\lceil w\div k\rceil=3\),第\(\lfloor w\div k+1\rfloor=3\)不能超过\(2^{w\bmod k}-1=1\)。
- 枚举位数:两位,从\(0\)到\(2^k-1\)中选\(2\)个,一共有\(C_{2^k-1}^{2}\)
- 继续枚举位数\(\cdots\)直到\(\lfloor w\div k\rfloor\)
- 枚举最高位的数:1,后面的数可选\(2\)到\(2^k-1\),一共有\(C_{2^k-2}^{2}\)
- 继续枚举最高位的数\(\cdots\)直到\(2^{w\bmod k}-1=1\)
- 输出答案
好了,就是这么一个过程,题目中也很清楚,要高精度,高精度的模板可以从高精度模板--zhengjun拿(我就是用这个模板过的)
最后说一句,高精度的除法比较慢,我一开始算\(C_n^m\)时用了这个公式:\(\cfrac{n!}{m!(n-m)!}\),结果就\(T\)掉了。
反正要算\(C_n^m\),\(n,m\)又不大,直接预处理出来放在数组里面用就可以了,用递推公式(不难理解):\(C_n^m=C_{n-1}^{m-1}+C_{n-1}^m\)。需要我说吗?
我还是唠一下吧。就是看这第\(n\)个数要不要选,如果选了,剩下的可能就是\(C_{n-1}{m-1}\),如果不选,就是\(C_{n-1}^m\)。
注意一下初始值:
所有的\(C_n^0\)都是\(1\),一个也不选就是一种情况。
好了,我太多嘴了
我的代码为了简洁,高精度模板自己去高精度模板--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;
}