P1066 [NOIP2006 提高组] 2^k进制数 题解

题目传送门

前置知识:组合数(杨辉三角),高精度(加法)

这道题用高精乘除爆算 C n m C_n^m Cnm 不用杨辉三角也是可以的,不过我们为方便起见统一用杨辉三角。

0x00 理解题意

以样例为例。

3 7

代表什么呢?题目给你了一个长度为 w = 7 w = 7 w=7 的二进制数(01 串),现在从右往左每 k = 3 k = 3 k=3 位一段划分成一个 ⌈ w / k ⌉ \lceil w/k\rceil w/k 位的 2 k 2^k 2k 进制数。最高位可能会不完整。

00 000 000

(假设划分成上面这几段)

每一小段都是一个独立的 2 k 2^k 2k 进制位,现在,求出所有 2 2 2 ⌈ w / k ⌉ \lceil w/k\rceil w/k 2 k 2^k 2k 进制数中,每一位的数码都严格小于其右边一位数码的数的个数。

比如这样是合法的:

01 010 011
=Decimal(123)

这样不合法:

01 011 010
=Decimal(132)

注意最高位不能是 0 0 0,也就是不重复。

0x01 组合数

C n m C_n^m Cnm ( n n n m m m) = n ! m ! ( n − m ) ! ={n!\over{m!(n-m)!}} =m!(nm)!n!

组合数有一条基本性质 C n m = C n − 1 m − 1 + C n − 1 m C_n^m=C_{n-1}^{m-1}+C_{n-1}^m Cnm=Cn1m1+Cn1m ,跟杨辉三角的递推式是一样的。所以用杨辉三角算组合数有两个好处:1.不容易爆 long long; 2.不用写高精度乘除。空间当然较大,但是本题空间足够了。

注意,根据定义,访问 C[n][m] 的值时,杨辉三角需要从下标 [0][0] 开始递推。

0x02 思路介绍

先不考虑最高位。注意到,在求 n n n 位的合法 2 k 2^k 2k 进制数的时候,相当于在求 C 2 k − 1 n C_{2^k-1}^n C2k1n 的值。

解释一下:

因为最高位不能是 0 0 0,所以最高位后面的所有位也不可能是 0 0 0

然后我们把 1... 2 k − 1 1...2^k-1 1...2k1 这所有的 2 k − 1 2^k-1 2k1 个合法数码排成一排,那么每一个长度为 n n n 的组合 C 2 k − 1 n C_{2^k-1}^n C2k1n(即, 2 k − 1 2^k-1 2k1 n n n)就对应了一个长为 n n n 2 k 2^k 2k 进制数,因为组合不讲顺序,所以这样正好和题目要求的 “只保留递增的序列” 一条吻合。

明白了这一点,我们不难写出统计长度从 2 2 2 ⌊ w / k ⌋ \lfloor w/k \rfloor w/k 的所有合法 2 k 2^k 2k 进制数个数的代码。

	scanf("%d %d",&k,&w);
	cnt=1<<k;//进制计算
	bit=w/k;//完整的位个数
	hi=w%k;//最高位的比特数
	init(cnt+1);//初始化杨辉三角
	hint res(0);//自己定义的高精度类 hint
	for(int i=2;i<=bit;i++)
		 res+=yh[cnt-1][i];//加上完整的位给答案带来的贡献

可表示为
∑ i = 2 b i t C 2 k − 1 i \sum_{i=2}^{bit}C_{2^k-1}^{i} i=2bitC2k1i

至于最高位的情况也不难。最高位是完整位的情况( h i = 0 hi = 0 hi=0),我们前面已经解决了。

易得,最高位的取值范围是 1 1 1 2 h i − 1 2^{hi}-1 2hi1 2 h i − 1 2^{hi}-1 2hi1 个。

那么次高位(一个完整位)的取值范围就是 2 2 2 2 h i 2^{hi} 2hi 。我们知道, 2 k > 2 h i 2^k>2^{hi} 2k>2hi,所以这些数码都是可取的。

原来在求 n n n 位的合法 2 k 2^k 2k 进制数的时候,最高完整位的起始值范围是从 1 1 1 2 k − 1 2^k-1 2k1 ,现在我们相当于把它的值域变成了 2 2 2 2 k − 1 2^k-1 2k1 3 3 3 2 k − 1 2^k-1 2k1 . . . ... ... 2 h i 2^{hi} 2hi 2 k − 1 2^k-1 2k1 ,并要求改变了值域后合法 2 k 2^k 2k 进制数的和。

类比前面的方法,把所有的合法数码排成一列求组合,不难得到总和就是

∑ i = 2 2 h i C 2 k − i b i t   ( 2 k − i > = b i t ) \sum_{i=2}^{2^{hi}} C_{2^k-i}^{bit}\ (2^k-i > =bit) i=22hiC2kibit (2ki>=bit)
代码实现如下(有对式子的轻微改动)

if(hi!=0)
for(int i=1;i<1<<hi;i++)
{
	if(cnt-1-i<bit) break;
	res+=yh[cnt-1-i][bit];
}

或者可以使用减法原理,直接求值,不用循环:

if(hi!=0)
	res+=yh[cnt-1][bit+1],res-=yh[cnt-(1<<hi)][bit+1];

(这个和等于第 bit+1 位完整时的贡献减去第 bit+1 位取了非法值的贡献)

(不完整)AC 代码如下:

#include <bits/stdc++.h>
#include "shadl.cpp"
using namespace std;
typedef high_decimal hint;
hint yh[605][605];
int cnt,bit,hi,k,w;
void init(int x)
{
	for(int i=0;i<=x;i++)
	{
		yh[i][0]=1,yh[i][i]=1;
		for(int j=1;j<i;j++)
			yh[i][j]=yh[i-1][j-1]+yh[i-1][j];
	} 
	return;
}
int main()
{
	scanf("%d %d",&k,&w);
	cnt=1<<k;
	bit=w/k;
	hi=w%k;
	init(cnt+1);
	hint res(0);
	for(int i=2;i<=bit;i++)
		 res+=yh[cnt-1][i];
//	for(int i=1;i<1<<hi;i++)
//	{
//		if(cnt-1-i<bit) break;
//		res+=yh[cnt-1-i][bit];
//	}
	if(hi!=0)
		res+=yh[cnt-1][bit+1],res-=yh[cnt-(1<<hi)][bit+1];
	res.output();
	return 0;
}

(请手动添加高精度类,这里采用了我自己的实现。)

0x03 总结

典型的组合数题目,难在理解题意和推性质。还要注意高精度的写法。

  • 37
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值