• [Luogu P1066] 2^k进制数 (组合数或DP)


     

    题面

    传送门:https://www.luogu.org/problemnew/show/P1066


    Solution

    这是一道神奇的题目,我们有两种方法来处理这个问题,一种是DP,一种是组合数。

    这题需要高精度,以下省略此声明

    .

    如果你对数学不感兴趣/喜欢写DP/(不想虐待自己),这里是DP做法。

    首先,我们可以发现,这个数最多有w/k位(向上取整),如下图所示:

    那么,我们就可以以这个特性做DP啦。

    设f[i][j]表示枚举到第i位(指2^k进制下的),最后一位数为j。

    f[i][j] = ∑ f[i-1][k] ((j==0 and k==0) or k<j) 

    这里的k显然是可以用前缀和优化的

    初始化 f[1][i]=1 (i=0~2^(w%k)-1)

    当然,还有一些小细节:f[倒数第2/第1个][0]=0

    答案为∑f[w/k][i] 

    (因为我没写过DP做法,这个做法纯口胡,如有错误请通知蒟蒻博主)


    那....组合数呢?

    事实上,这题的组合数做法的确很妙,(当然也有不少细节)

    假设我们枚举了第一位数,那么后面位数的方案数是可以通过组合数来计算出来的。

    因为后面的数要比第一位大,那么后面的数相当于从 [第一位数+1,2^k-1] 这个数的区间中选出x个数(x为后面的位数数量)来 (因为每一种方案都可以通过摆成升序满足题目要求)。

    但是考虑到有可能有若干个前导零,我们还要枚举第一个位数从哪开始。

    因为枚举了前导零,我们枚举第一位数时应该从1开始(从0开始会有重复)

    这样子,答案为:

    (事实上口胡起来简单,写起来还有很多细节,这得亲自体会然后就会感到这题的毒瘤)

    就酱,我们就可以切掉嘴巴AC出这道题啦(~ ̄▽ ̄)~ 


    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    struct Int128
    {
        static const int N=500;
        int a[N],len;
        Int128()
        {
            memset(a,0,sizeof a);
            len=0;
        }
        void Print()
        {
            for(int i=len;i>=1;i--)
                printf("%d",a[i]);
        }
        friend Int128 operator * (Int128 A,int B)
        {
            for(int i=1;i<=A.len;i++)
                A.a[i]*=B;
            bool IsFullZero=true;
            for(int i=1;i<=A.len;i++)
            {
                if(A.a[i]>=10)
                {
                    A.a[i+1]+=A.a[i]/10,A.a[i]%=10;
                    if(i==A.len and A.a[i+1]!=0)
                        A.len++;
                }
                if(A.a[i]!=0) IsFullZero=false;
            }
            if(IsFullZero==true) A.len=1;
            return A;
        }
        friend Int128 operator / (Int128 A,int B)
        {
            Int128 ans;
            int temp=0;
            for(int i=A.len;i>=1;i--)
            {
                temp=temp*10+A.a[i];
                if(temp>=B)
                {
                    ans.a[i]=temp/B,temp=temp%B;
                    ans.len=max(ans.len,i);
                }
            }
            return ans;
        }
        friend Int128 operator + (Int128 A,Int128 B)
        {
            if(A.len<B.len) swap(A,B);
            for(int i=1;i<=A.len;i++)
            {
                A.a[i]=A.a[i]+B.a[i];
                if(A.a[i]>9)
                {
                    A.a[i+1]++;A.a[i]-=10;
                    if(i==A.len)
                        A.len++;
                }
            }
            return A;
        }
    };
    const int N=1<<(9+1);
    Int128 C[N];
    int n,x,K,w,first,m;
    int main()
    {
        scanf("%d%d",&K,&w);
        
        first=1<<(w%K),x=w/K;
        if(w%K==0) 
            first=1<<K,x--;
        m=1<<K;
        
        
        Int128 ans;
        for(int j=0;j<=x-1;j++)
        {
            int tx=x-j;
            memset(C[tx].a,0,sizeof C[tx].a);
            C[tx].a[1]=1,C[tx].len=1;
            for(int i=tx+1;i<=m;i++)
            {
                memset(C[i].a,0,sizeof C[i].a);
                C[i]=(C[i-1]*i)/(i-tx);
            }
            if(j!=0) first=m;
            for(int i=1;i<m and i<first;i++)
            {
                if(m-1-i<tx) break;
                ans=ans+C[m-1-i];
            }
            //ans.Print();
            //cerr<<endl;
        }
            
        ans.Print();
        return 0;
    }
    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    攻城狮在路上(壹) Hibernate(五)--- 映射一对多关联关系
    攻城狮在路上(壹) Hibernate(四)--- 对象标识符(OID)生成机制
    SQL常用方言列表
    maven 依赖查询
    攻城狮在路上(壹) Hibernate(三)--- 属性访问、命名策略、派生属性、指定包名等
    攻城狮在路上(壹) Hibernate(二)--- 第一个hibernate程序
    《精通Hibernate:Java对象持久化技术详解》目录
    攻城狮在路上(壹) Hibernate(一)--- 软件环境、参考书目等一览表
    <转>Hibernate的优、缺点(局限性)
    攻城狮在路上(零)开篇
  • 原文地址:https://www.cnblogs.com/GoldenPotato/p/9601826.html
Copyright © 2020-2023  润新知