【2^k进制数】

发现自己推得组合数好像不太一样

先把这个复杂的柿子写一遍

\[\sum_{i=2}^{\left \lfloor\frac{n}{k}\right \rfloor}C_{2^k-1}^{i}+\sum_{i=1}^{2^{n\text{ } \text{mod} \text{ }k}-1}C_{2^k-1-i}^{\left \lfloor\frac{n}{k}\right \rfloor}\]

感觉这个柿子非常蛇皮

但是非常好求啊

由于\(2^k-1\)非常小,最大仅仅是\(511\),所以我们没有什么必要预处理阶乘,我们可以直接用组合数递推的方式来做

于是不需要打高精除或者高精乘了,一个高精加就够了

于是做法就非常无脑了,重要的是这个柿子是怎么推出来的

首先我们先考虑一个非常弱化的版本,就是\(k|n\)

如果\(k|n\)的话,那么这个长度为\(n\)的二进制数就能被恰好分成\(n/k\)个块,而且每一个块能选择的数都是\(0\)\(2^k-1\)\(2^k\)个数

我们发现\(0\)这个非常不好考虑,于是我们可以先忽略掉\(0\)

所以现在有\(n/k\)个块,每个块内能填\(2^k-1\)种数

那么就有\(C_{2^k-1}^{n/k}\)种可能

之后我们再来考虑\(0\)的情况,首先最高位(如果不是第二位的话)是可以填\(0\)的,而剩下的\(n/k-1\)个块我们仍旧按照之前的方式来填,于是就有\(C_{2^k-1}^{n/k-1}\),之后对于次高位还是可以填\(0\)(同时最高位也填\(0\)),那么还有\(n/k-2\)个块,于是就是\(C_{2^k-1}^{n/k-2}\)

以此类推,直到对于第三个的块,我们还是可以填将这个块以及之前所有的块都填\(0\),那么就还有\(2\)个块,于是就是\(C_{2^k-1}^{2}\)

而第二个块可是不能填\(0\)了,于是就没了

所以对于\(k|n\)的时候,答案就是

\[\sum_{i=2}^{n/k}C_{2^k-1}^{i}\]

之后我们再来考虑一下\(k\)不整除\(n\)的情况

这个样子的话一共会分成\(\left \lfloor\frac{n}{k}\right \rfloor+1\)个块,\(\left \lfloor\frac{n}{k}\right \rfloor\)个块内可以选择的数都是\(0\)\(2^k-1\)\(2^k\)个数,而最后一个不完整的块只有\(n\text{ } \text{mod} \text{ }k\)位,所以能选择的数只有\(0\)\(2^{n\text{ } \text{mod} \text{ }k}-1\)

如果这个最高位选择填\(0\)那么退化成了\(k|n\)的情况,所以最高位填0的方案数为

\[\sum_{i=2}^{\left \lfloor\frac{n}{k}\right \rfloor}C_{2^k-1}^{i}\]

之后最高位还有\(1\)\(2^{n\text{ } \text{mod} \text{ }k}-1\)这些数可以填,如果我们选择填\(i\)的话,那么剩下的块内就不能填比\(i\)小的数,于是剩下的每个块内能选择的就有\(2^k-1-i\)个数,所以方案数就是\(C_{2^k-1-i}^{\left \lfloor\frac{n}{k}\right \rfloor}\)

所以最后的答案还应该加上

\[\sum_{i=1}^{2^{n\text{ } \text{mod} \text{ }k}-1}C_{2^k-1-i}^{\left \lfloor\frac{n}{k}\right \rfloor}\]

代码

#include<cstring>
#include<string>
#include<cstdio>
#include<iostream>
#define re register
#define maxn 512
using namespace std;
string c[maxn][maxn];
int n,k;
int p,t;
int res;
int aa[201],bb[201],cc[201];
inline string sum(string a,string b)
{
    memset(aa,0,sizeof(aa));
    memset(bb,0,sizeof(bb));
    memset(cc,0,sizeof(cc));
    int lena=a.size();
    int lenb=b.size();
    for(re int i=0;i<lena;i++)
        aa[i+1]=a[lena-i-1]-48;
    for(re int i=0;i<lenb;i++)
        bb[i+1]=b[lenb-i-1]-48;
    int p=1;
    for(p=1;p<=max(lena,lenb)||cc[p];p++)
    {
        cc[p]+=aa[p]+bb[p];
        cc[p+1]+=cc[p]/10;
        cc[p]%=10;
    }
    string C="\0";
    for(re int i=p-1;i;i--)
        C+=char(cc[i]+48);
    return C;
}
int main()
{
    scanf("%d%d",&k,&n);
    p=n/k;
    res=n%k;
    t=(1<<k);
    c[0][0]="1";
    for(re int i=1;i<=t-1;i++)
        c[i][0]=c[i][i]="1";
    for(re int i=1;i<t;i++)
    for(re int j=1;j<i;j++)
        c[i][j]=sum(c[i-1][j-1],c[i-1][j]);
    string ans="0";
    for(re int i=2;i<=p;i++)
    {
        if(i>t-1) break;
        ans=sum(ans,c[t-1][i]);
    }
    int pp=(1<<res)-1;
    for(re int i=1;i<=pp;i++)
    {
        if(p>t-1-i) break;
        ans=sum(ans,c[t-1-i][p]);
    }
    cout<<ans<<endl;
    return 0;
}

转载于:https://www.cnblogs.com/asuldb/p/10207756.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值