[VIJOS2055][SDOI2019]移动金币:DP+组合数学

分析

显然可以转化为阶梯nim。

于是问题转化为了对于所有\(i \in [0,n-m]\),求长度为\(\lfloor\frac{m+1}{2}\rfloor\),和为\(i\),异或和非\(0\)的非负整数序列的个数。

直接DP看似不太可行,然而UOJ群的dalao们告诉博主可以按位DP。

\(f[i][j][0/1]\)表示考虑了后\(i\)位,当前的和为\(j\),后\(i\)位的异或和是否为\(0\)的方案数,转移时枚举当前位有多少个\(1\),类似数位DP那样就好。

最后用隔板法统计答案即可。

记搜的话直接记搜可能过不去,加些剪枝就好了。

代码

#include <bits/stdc++.h>

#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define mkpr std::make_pair
#define fi first
#define se second
#define lowbit(a) ((a)&(-(a)))
typedef long long LL;

using std::cerr;
using std::endl;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

const int MOD=1e9+9;
const int MAXN=150005;

int n,m,cnt;
int fac[MAXN+50],invf[MAXN+50];
int f[20][MAXN][2];

inline int qpow(int x,int y){
    int ret=1,tt=x%MOD;
    while(y){
        if(y&1)ret=1ll*ret*tt%MOD;
        tt=1ll*tt*tt%MOD;
        y>>=1;
    }
    return ret;
}

inline int binom(int n,int m){
    if(n<0||m<0||n<m)return 0;
    return 1ll*fac[n]*invf[n-m]%MOD*invf[m]%MOD;
}

int dfs(int pos,int sum,int have1){
    if(sum&((1<<pos)-1))return 0;
    if(pos>17){
        if(sum==0&&have1)return 1;
        else return 0;
    }
    if(f[pos][sum][have1]!=-1)return f[pos][sum][have1];
    int ret=0;
    rin(i,0,cnt){
        if((1ll<<pos)*i>sum)break;
        ret=(ret+1ll*dfs(pos+1,sum-(1ll<<pos)*i,have1|(i&1))*binom(cnt,i))%MOD;
    }
    return f[pos][sum][have1]=ret;
}

void init(){
    fac[0]=1;
    rin(i,1,n+m)fac[i]=1ll*fac[i-1]*i%MOD;
    invf[n+m]=qpow(fac[n+m],MOD-2);
    irin(i,n+m-1,0)invf[i]=1ll*invf[i+1]*(i+1)%MOD;
}

int main(){
    memset(f,-1,sizeof f);
    n=read(),m=read();init();
    cnt=(m+1)/2;
    int ans=0,box=(m&1)==0?cnt+1:cnt;
    rin(i,0,n-m){
        int rem=n-m-i;
        ans=(ans+1ll*dfs(0,i,0)*binom(rem+box-1,box-1))%MOD;
    }
    printf("%d\n",ans);
    return 0;
}

posted on 2019-05-08 08:46 ErkkiErkko 阅读(...) 评论(...) 编辑 收藏

统计