D - Carry Bit 组合数学

98 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第16天,点击查看活动详情

D - Carry Bit 组合数学

思路:设A,B,C分别为数x的二进制数组,数y的二进制数组和x+y的进位数组,对于c[i]来说

C[i]=(A[i]+B[i]+C[i-1])>=2,那么可以讨论一下,

如果C[i]=C[i-1]=0,那么C[i],B[i]取值为(0,0),(1,0),(0,1);

如果C[i]=C[i-1]=1,那么A[i],B[i]取值为(1,1),(1,0),(0,1);

如果C[i]=1,C[i-1]=0,那么A[i],B[i]的取值为(1,1);

如果C[i]=0,C[i-1]=1,那么A[i],B[i]的取值为(0,0);

当C[i]=C[i-1]时会有A和B的取值会有三种情况,对于长度为n+1的C数组,设有P个位置是C[i]!=C[i-1],那么就要3^(n-p)种取值情况,而因为C[0]=0,所以c数组一定是000111000....即从0开始且可以看出一共有p+1段,有(p+1)/2段是1段,其他的都是0段,然后现在的问题就是要把k个1划分成(p+1)/2段,n+1-k个0划分成(n-(p+1)/2)各段,因为C数组长度为n+1所以是n+1-k而不是n-k,对于把x个数划分成y段的公式是C(x-1,y-1),对于x==0,y==0的时候特判一下就可以,另外这个p是设的,可能会有多个取值,从0到n枚举就行

#include<bits/stdc++.h>
#define int long long
#define ll __int128
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define pause system("pause")
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
const int inf=1e18;
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int getinv(int a){return qpow(a,mod-2);}
int th[N],fac[N],ifac[N],n,k;
void init()
{
    fac[0]=1;th[0]=1;
    for(int i=1;i<=1000000;i++) th[i]=th[i-1]*3LL%mod,fac[i]=fac[i-1]*i%mod;
    ifac[1000000]=getinv(fac[1000000]);
    for(int i=999999;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
}
int C(int a,int b)
{
    if(a<b||b<0) return 0;
    return ((fac[a]*ifac[a-b]%mod)*ifac[b]%mod);
}
int divi(int a,int b)
{
    if(a==0&&b==0) return 1;
    return C(a-1,b-1);
}
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>k;
    init();
    int ans=0;
    for(int p=0;p<=n;p++)
    {
        ans=(ans+(th[n-p]*divi(k,(p+1)/2)%mod)*divi(n-k+1,p+1-(p+1)/2)%mod)%mod;
    }
    cout<<ans<<endl;
    return 0;
}