• NOIP2006 2^k 进制数


    题目链接

    Solution

    用 jio 推柿子,用心卡内存...

    先考虑限制条件 3,它等同于选出的数必须小于 (2^w)。如果直接把 (2^w) 转化成 (2^k) 进制,写高精比较麻烦。手模一下转化进制的过程,发现除去最后一次,每次 (\%) 得到的都是 (0)。因此稍微找规律得到:转化为 (2^k) 进制后的位数为 (lfloor { frac{w}{k}} floor + 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;
    }
    
  • 相关阅读:
    视频直播思路
    Swift 算法实战之路:栈和队列
    多线程(RunLoop)
    Charle抓包与wireshark使用
    CoreData归纳使用
    支付宝接入心得(流程)
    TableView的性能优化
    app启动页问题
    公司的开发者账号申请
    java关于时间的笔记
  • 原文地址:https://www.cnblogs.com/Andy-park/p/13575747.html
Copyright © 2020-2023  润新知