zoukankan      html  css  js  c++  java
  • [VIJOS2055][SDOI2019]移动金币:DP+组合数学

    分析

    显然可以转化为阶梯nim。

    于是问题转化为了对于所有(i in [0,n-m]),求长度为(lfloorfrac{m+1}{2} floor),和为(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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Java复习笔记8--内部类
    Java复习笔记7--Java封装
    Java复习笔记6--Final&Static
    Java复习笔记5--equals方法总结
    Java复习笔记4--实现多重继承
    Java复习笔记3--强制类型转换
    Java复习笔记2--匿名类和多态
    python版本管理--pyenv
    gensim自然语言处理(续)
    gensim自然语言处理
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10829766.html
Copyright © 2011-2022 走看看