[BZOJ 3823]定情信物

题面

定情信物

题解

  这题主要考高中物理和数学。
  首先定义 \(f[i][j]\) 表示 \(i\) 维超立方体中第 \(j\) 维元素的数量,根据实际意义,我们可以推出递推式: \(f[i][j]=2\cdot f[i-1][j]+f[i-1][j-1]\)

   \(i\) 维超立方体是由 \(i\!-\!1\) 维超立方体平移得来的,那么第 \(j\) 维元素来源为原来的第 \(j\) 维元素并复制了一份,加上原来的第 \(j\!-\!1\) 维元素通过平移而新构成了一部分。——Ezio

  然后我们考虑其组合意义,发现 \(f[i][j]\) 表示把 \(i\) 个元素分为 \(j\) 份,每份第一个元素贡献为 \(1\) ,其它元素贡献为 \(2\) 。那么我们就可以定义 \(g[i][j]\) 表示把 \(i\) 个元素分为 \(j\) 份,每个元素贡献都为 \(2\) ,也就是说 \(g[i][j]=2^i\cdot C_i^j\) ,并且有 \(g[i][j]=2^j\cdot f[i][j]\) ,那么 \(f[i][j]=2^{i-j}\cdot C_i^j\) ,然后就能直接算了,注意要用 \(\text{Lucas}\)

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
const int maxn=1e7+3;
int n,MOD;
int bin[maxn];
int Fac[maxn];
int Inv[maxn];
inline int Pow(int x,int k){
    int ans=1;
    for(;k;x=x*1ll*x%MOD,k>>=1)
        if(k&1)ans=ans*1ll*x%MOD;
    return ans;
}
inline int C(int n,int m){return Fac[n]*1ll*Inv[m]%MOD*Inv[n-m]%MOD;}
inline int Lucas(int n,int m){
    if(n<MOD && m<MOD)
        return n<m ? 0 : C(n,m);
    const int nn=n%MOD,mm=m%MOD;
    return nn<mm ? 0 : C(nn,mm)*1ll*Lucas(n/MOD,m/MOD)%MOD;
}
int main(){
    scanf("%d%d",&n,&MOD);
    int M=std::min(MOD-1,n);
    for(int i=Fac[0]=1;i<=M;++i)
        Fac[i]=Fac[i-1]*1ll*i%MOD;
    Inv[M]=Pow(Fac[M],MOD-2);
    for(int i=M-1;~i;--i)
        Inv[i]=Inv[i+1]*1ll*(i+1)%MOD;
    for(int i=bin[0]=1;i<=n;++i)
        bin[i]=(bin[i-1]<<1)%MOD;
    int ans=0;
    for(int i=0;i<=n;++i)
        ans^=bin[n-i]*1ll*Lucas(n,i)%MOD;
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/hahamengbier/p/bzoj-3823.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值