P1066 2^k进制数

P1066 2^k进制数

高精+组合数学

读题可得,满足条件的2^k进制数可分为2种

1.位数为2~w/k的

2.位数为w/k+1(w%k!=0)的

对于第一种,就是求2^k-1个数中不重复取 i 个的组合数,就是C(2^k-1,2)+...+C(2^k-1,w/k).

对于第二种,由于题目限制每一位严格小于右边相邻的一位,所以取数时只能越来越小,即C(2^k-1-i,w/k),其中 i 为最高位的所有可取值1~2^(w%k)-1

高精需要压位,不然会MLEqwq

#include<cstdio>
#include<cstring>
using namespace std;
const int wid=10000; //压4位
struct data{ //个人觉得重载好写qwq
    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;
        u.len=len+tmp.len-1;
        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>=1;--i){
            for(int j=10;a[i]*j<wid;j*=10) printf("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; //注意防止上限溢出2^9=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]+c[i-1][j-1]; //预处理组合数
    for(int i=2;i<=p;++i) ans=ans+c[n][i]; //第一类
    for(int i=(1<<w%k)-1;i>=1;--i) ans=ans+c[n-i][p];//第二类
    ans.print();
    return 0;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/9556220.html