zoukankan      html  css  js  c++  java
  • 2^k进制数

    题目描述

    (S) 是长度为 (w)(01) 串。从串的右边开始,每 (k) 个字符分成一段(最后不够 (k) 个字符的也分成一段),组成一个小于 (2^k) 的数。然后这 (leftlceilfrac{w}{k} ight ceil) 个数将组成一个序列。若这个序列除去前导零后的长度不小于 (2) 且序列里的数严格递增,那么 (S) 就是一个合法的串。求所有合法的串的个数。

    解法

    计数题。一开始就往组合方面想了。这个直觉似乎是对的。

    先除去第一个数不看,即强制选 (0)(因为它可能不是 (2^k),比较特殊),那么对于后面的 (lfloor frac{w}{k} floor) 个数,如果直接取的话,每个数都可以取 (0) ~ (2^p-1),要满足严格递增,实际上就是在这 (2^p) 个数里面选 (lfloor frac{w}{k} floor) 个来组成序列,那么方案就是

    [inom{2^p}{lfloor frac{w}{k} floor} ]

    这样对吗?你会发现这是错的。因为可以有前导零,而且前导零并不是单增的。这就提示我们将前导零抛开,分步计算。现枚举一个 (i)强制(i) 个数不为 (0),有 (2^k-1) 种,前面剩下的数全部是 (0),有 (1) 种,那么总方案数实际上是

    [sum_{i=2}^{lfloor frac{w}{k} floor} inom{2^k-1}{i} ]

    (序列长度大于等于 (2)(i) 要从 (2) 开始枚举)
    现在将第一个数不为 (0) 的方案加进来,枚举 (i) 表示第一个数选什么,那么对于后面 (lfloor frac{w}{k} floor) 个数,就在大于 (i) 的数里面选。方案就有

    [sum_{i=1}^{2^{b mod k}} inom{2^k-1-i}{lfloor frac{w}{k} floor} ]

    答案就是

    [sum_{i=2}^{lfloor frac{w}{k} floor} inom{2^k-1}{i} + sum_{i=1}^{2^{b mod k}} inom{2^k-1-i}{lfloor frac{w}{k} floor} ]

    考虑怎么求这个东西,我们发现 (k) 是一个很小的数,而对于 (n<m)(inom{n}{m}=0),所以只需要预处理出 (n<2^9) 以内的组合数,用公式直接递推即可。

    Tips

    推完式子之后发现要写高精,长度范围在 (200) 以内。然后写了高精加之后,直接交了,全部 (MLE)。一算空间 (2^9 imes 2^9 imes 200 imes 4approx 200 MB)。然后我就想这道题不能预处理,就开始写暴力求组合数(以及高精乘和高精除)。写到一半觉得不可能有这么麻烦,就去翻题解,发现某大佬用的 (string) 来存的高精数组。得到启发后,将 (int) 换成了 (char),空间直接减成 (50MB)。一发过了。

    Code

    (加了一些边界判定,感性理解即可)

    #include<stdio.h>
    #include<string.h>
    #define N (1<<9)
    
    int n,k;
    
    inline int max(int x,int y){return x>y? x:y;}
    struct Big{
        char num[201],len;
        Big(int x=0){memset(num,0,sizeof(num)); len=0; while(x) num[++len]=x%10,x/=10;}
        Big operator +(const Big x){
            Big c; int cur=0;
            c.len=max(x.len,len);
            for(int i=1;i<=c.len;i++){
                c.num[i]+=num[i]+x.num[i]+cur;
                cur=c.num[i]/10;
                c.num[i]%=10;
            }
            if(cur) c.num[++c.len]=cur;
            return c;
        }
        inline void print(){
            for(int i=len;i>=1;i--)
                printf("%d",num[i]);
        }
    }C[N][N];
    
    inline int min(int x,int y){return x<y? x:y;}
    int main(){
        scanf("%d%d",&k,&n);
        for(int i=0;i<512;i++) C[i][0]=Big(1),C[i][i]=Big(1);
        for(int i=1;i<512;i++)
            for(int j=1;j<i;j++)
                C[i][j]=C[i-1][j]+C[i-1][j-1];
        if(k>=n) printf("0");
        else{
            Big ans=Big(0);
            int p=1<<k; int rg=min(n/k,p-1);
            for(int i=2;i<=rg;i++)
                ans=ans+C[p-1][i];
            if(n%k&&p-1>n/k){
                int p_=1<<(n%k);
                for(int i=1;i<p_&&p-i-1>=n/k;i++)
                    ans=ans+C[p-i-1][n/k];
            }
            ans.print();
        }
    }
    // 3 7
    
  • 相关阅读:
    兼容利器之X-UA-Compatible
    SQL Server之游标的基础知识
    SQL 按特定字段值排序
    NodeJS 开篇 牛刀小试
    临时表之IF-ELSE
    css中如何设置字体
    怎么做到不加班?
    C# .net中cookie值为中文时的乱码解决方法
    HTML5商城开发一 楼层滚动加载数据
    QQ浏览器X5内核问题汇总
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14029481.html
Copyright © 2011-2022 走看看