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;
}
posted @ 2020-08-28 09:35  Nyxia  阅读(165)  评论(0编辑  收藏  举报