NOIP2006 2^k 进制数
Solution
用 jio 推柿子,用心卡内存...
先考虑限制条件 3,它等同于选出的数必须小于 \(2^w\)。如果直接把 \(2^w\) 转化成 \(2^k\) 进制,写高精比较麻烦。手模一下转化进制的过程,发现除去最后一次,每次 \(\%\) 得到的都是 \(0\)。因此稍微找规律得到:转化为 \(2^k\) 进制后的位数为 \(\lfloor { \frac{w}{k}} \rfloor + 1\),其最高位为 \(2^{w \% k}\),其余位都是 \(0\),记为 \(n\) 位的数 \(A\)。
现在需要统计严格递增的小于 \(A\) 的数的个数。分为下面两种情况:
注意:
按照位数讨论,取数时删去 0。如果选择 0,按照递增顺序,它一定在最高位,这种情况将在位数更少时讨论,造成重复。
之所以用组合而不用排列,是因为取出(互不相同的)若干个数字之后,要想按照严格递增顺序,有且仅有一种合法的排列方式。
1. 所有位数 \(x\) 满足 \(1<x<n\) 的数
不管怎么取,当前数一定小于 \(A\)。在除去 0 的 \(2^k\) 进制中任意取数,方案数为 \(C_{2^k - 1}^x\)。
2. 所有位数 \(x\) 满足 \(x=n\) 的数
一般来说,这种情况需要进行多次讨论,复杂度将会大幅升高。但是本题情况特殊,即除第一位以外后面位都是 0,这个性质也可以看成:位数为 \(n\) 的数要想比 \(A\) 小,其最高位一定比 \(A\) 的最高位小。所以我们枚举其最高位 \(i\) 满足 \(1<i<2^{w\%k}\),对于每个 \(i\),后面的数都有 \(C_{2^k-i-1}^{n-1}\) 种取法。
组合数可以用 \(C_{i,j}=C_{i-1,j}+C_{i-1,j-1}\) 这个公式推出,因此以上所有代码实现只需要用到高精加。最后注意,用 int 写高精会炸内存,可以用 string 来写。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 1001;
int k, w, len, base[2333];
int a[2333], b[2333], c[2333];
string C[N][N], Ans = "";
string add(string x, string y)
{
string res = "";
int len1 = x.size(), len2 = y.size(), len3 = max(len1, len2);
memset(c, 0, sizeof(c));
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
for(int i = 0; i < len1; i++) a[i + 1] = x[len1 - i - 1] - '0';
for(int i = 0; i < len2; i++) b[i + 1] = y[len2 - i - 1] - '0';
for(int i = 1; i <= len3; i++)
{
c[i] += a[i] + b[i];
c[i + 1] += c[i] / 10;
c[i] %= 10;
}
while(c[len3 + 1]) len3++;
for(int i = len3; i > 0; i--) res += c[i] + 48;
return res;
}
int main()
{
scanf("%d%d", &k, &w);
len = w / k + 1;
base[0] = 1;
for(int i = 1; i <= 9; i++) base[i] = base[i - 1] * 2;
for(int i = 0; i <= base[9]; i++) // string 类型写高精需要注意初始化
for(int j = 0; j <= base[9]; j++)
C[i][j] = "0";
C[1][1] = "1";
for(int i = 1; i <= base[9]; i++) C[i][0] = "1";
for(int i = 2; i <= base[9]; i++)
for(int j = 1; j <= i; j++)
C[i][j] = add(C[i - 1][j], C[i - 1][j - 1]);
for(int i = 2; i < len; i++) Ans = add(Ans, C[base[k] - 1][i]);
for(int i = 1; i < base[w % k]; i++) Ans = add(Ans, C[base[k] - i - 1][len - 1]);
cout << Ans;
return 0;
}