【SDOI2019】移动金币(阶梯NIM)(DP)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zxyoi_dreamer/article/details/102742798

传送门


题解:

很容易注意到这是一个阶梯NIM,先手必胜当且仅当所有奇数位置上的石头数量的xor和不为 0 0

按照套路,考虑拆位,由于异或和不为 0 0 不太好算,考虑算异或和为 0 0 ,即奇数位置上每一位的数量都是偶数。

首先可以 O ( m 2 ) O(m^2) 处理出在某一位上一共放了 j j 1 1 ,且奇数位置上的总数为偶数的方案数。

然后考虑两种DP,考虑前 i i 位,当前总和为 j j 的方案数,转移是 O ( n m log n ) O(nm\log n) 的。

但是实际上可以直接考虑设 f [ i ] [ j ] f[i][j] 表示考虑前 i i 位,总和距离 n n 还差 j 2 i 1 j*2^{i-1} 的方案数, j j 的上界显然不超过 m m 。转移是 O ( m 2 log n ) O(m^2\log n) 的,注意到是一个卷积,可以利用多项式乘法优化到 O ( m log m log n ) O(m\log m\log n) ,不过这道题由于 m m 过小但卷积常数过大没什么用。

综上,这道题理论上是可以做到 O ( m 2 + m log m log n ) O(m^2+m\log m\log n) 的。可以拿来出毒瘤题了

m m 给5000, n n 给1e18可能要开3s左右。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=1e9+9;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b){
	int r=1;for(;b;b>>=1,a=mul(a,a))
	if(b&1)r=mul(r,a);return r;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline void ex_gcd(int a,int b,int &x,int &y){
	if(!b){x=1,y=0;return ;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}
inline int Inv(int a){
	int x,y;ex_gcd(mod,a,y,x);
	return x+(x>>31&mod);
}

cs int M=63;

int n,m;
int fac[M],ifac[M];
inline void init_fac(){
	fac[0]=fac[1]=1;
	for(int re i=2;i<M;++i)fac[i]=mul(fac[i-1],i);
	ifac[M-1]=Inv(fac[M-1]);
	for(int re i=M-1;i;--i)ifac[i-1]=mul(ifac[i],i);
}
inline int C(int n,int m){return n>=0&&m>=0&&n>=m?mul(fac[n],mul(ifac[m],ifac[n-m])):0;}

int f[M][M],g[M];
inline int work(int n,int m){
	int od=m>>1,ev=m+1>>1;f[18][0]=1;
	for(int re i=0;i<=m;++i)
	for(int re j=0;j<=i;j+=2)Inc(g[i],mul(C(od,j),C(ev,i-j)));
	for(int re i=17;~i;--i)
	for(int re j=0;j<=m;++j)if(f[i+1][j])
	for(int re li=(j*2)+(n>>i&1),k=std::max(0,li-m);k<=m&&k<=li;++k)
	Inc(f[i][li-k],mul(f[i+1][j],g[k]));
	return f[0][0];
}

inline int binom(int n,int m){int r=ifac[m];for(int re i=n;i>n-m;--i)Mul(r,i);return r;}

signed main(){
#ifdef zxyoi
	freopen("coin.in","r",stdin);
#endif
	scanf("%d%d",&n,&m);init_fac();
	cout<<dec(binom(n,m),work(n-m,m+1));
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/102742798
今日推荐