• P1066 2^k进制数


    传送门

    好好看题

    看懂了就不难了

    在2^k进制下,一位一位看

    每一位都有一些数可以填

    除非是最左边一位,不然可以填的数最大都是 2^k-1

    所以显然当填的位数为 i 时(不是最后一位),可能的选取方案总共有 C(2^k-1,i )

    如果要填最后一位

    最后一位可以填的最大的数为 2^ (w%k)-1

    那就枚举一下最后的数,前面选取的数都要大于它

    那么当最后的数为 i 时,前面的数选取方案数为 C( (2^k-1) -i,w/k)

    (注意 w/k 可能很大,但是如果超过 2^9-1 那就没意义了(能够选的数最大只有 2^9-1))

    因为答案很大,要用高精度

    我用的是压位高精(借着这一题学了一下压位高精...)

    (感谢crk大佬为我提供压位高精的代码 %%%)

    最后要注意一下细节

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int wid=10000;//压位高精,每4位放一起
    struct data
    {
        int a[52],len;
        data(){ memset(a,0,sizeof(a)); len=0; }
        //初始化
        data operator + (data &tmp)//重载加号
        {
            data u; int x=0;
            u.len= len>tmp.len ? len : tmp.len;
            for(int i=1;i<=u.len;i++)
            {
                u.a[i]=a[i]+tmp.a[i]+x;
                x=u.a[i]/wid; u.a[i]%=wid;
            }
            while(x) u.a[++u.len]=x%wid,x/=wid;
            return u;
        }
        data operator * (data &tmp)
        //重载乘号,其实用不上,顺便一起打了
        {
            data u; int x=0;
            for(int i=1;i<=tmp.len;i++,x=0)
            {
                for(int j=1;j<=len;j++)
                {
                    u.a[i+j-1]+=tmp.a[i]*a[j]+x;
                    x=u.a[i+j-1]/wid; u.a[i+j-1]%=wid;
                }//注意细节
                u.a[i+len]+=x;
            }
            while(u.a[u.len+1]) u.a[u.len+2]+=u.a[u.len+1]/wid,u.a[(++u.len)+1]%=wid;
            //一堆细节..
            return u;
        }
        void print()
        {
            printf("%d",a[len]);
            for(int i=len-1;i;i--)
            {
                for(int j=10;j*a[i]<wid;j*=10) printf("0");
                //如果压的数还不到4位就要补上0
                printf("%d",a[i]);
            }
        }
    }C[513][513],ans;
    int k,w,n,p;
    int main()
    {
        scanf("%d%d",&k,&w);
        n=(1<<k)-1; p=w/k>512 ? 512 : w/k;//p最大为512
    
        for(int i=0;i<=n;i++) C[i][0].a[1]=1,C[i][0].len=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++)
                C[i][j]=C[i-1][j-1]+C[i-1][j];
        //求组合数
        for(int i=2;i<=p;i++) ans=ans+C[n][i];
        for(int i=(1<<w%k)-1;i;i--) ans=ans+C[n-i][p];//i不能取0,细节
        ans.print();
        return 0;
    }
  • 相关阅读:
    『笔记』数学数论(八)
    『笔记』BSGS
    『笔记』组合数学(六)
    01 分数规划
    高斯消元
    拉格朗日插值法
    洛谷网课数论
    [IOI2013]robots 机器人
    P3530 [POI2012]FES-Festival
    NOIP 2015 day1
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9605859.html
Copyright © 2020-2023  润新知