洛谷 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\)

然后就是一个十分简单的组合数。

让我们模拟一遍(还是样例):

  1. 求出位数\(\lceil w\div k\rceil=3\),第\(\lfloor w\div k+1\rfloor=3\)不能超过\(2^{w\bmod k}-1=1\)
  2. 枚举位数:两位,从\(0\)\(2^k-1\)中选\(2\)个,一共有\(C_{2^k-1}^{2}\)
  3. 继续枚举位数\(\cdots\)直到\(\lfloor w\div k\rfloor\)
  4. 枚举最高位的数:1,后面的数可选\(2\)\(2^k-1\),一共有\(C_{2^k-2}^{2}\)
  5. 继续枚举最高位的数\(\cdots\)直到\(2^{w\bmod k}-1=1\)
  6. 输出答案

好了,就是这么一个过程,题目中也很清楚,要高精度,高精度的模板可以从高精度模板--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;
}

谢谢--zhengjun

posted @ 2022-06-10 19:27  A_zjzj  阅读(32)  评论(0编辑  收藏  举报