正题
题目链接:https://www.luogu.com.cn/problem/P5363
题目大意
1×n1\times n1×n的网格上有mmm个硬币,两个人轮流向前移动一个硬币但是不能超过前一个硬币,无法移动者输。
求有多少种情况先手必胜。
解题思路
竟然有我会的题,我感动
位置做差分再减去111之后就是一个经典的阶梯博弈问题了,结论就是奇数位置的异或和。
但是这题是计数,先让nnn减去mmm,然后正难则反考虑求总方案和后手必胜的情况,这样问题就变为有多少个长度为mmm的非负整数序列满足它们的和不超过nnn且奇数位置的异或和为000。
考虑枚举奇数位置的和,奇数位置个数为z=⌊m+12⌋z=\lfloor\frac{m+1}{2}\rfloorz=⌊2m+1⌋,设fif_ifi表示zzz个数的和为iii时异或和为000的方案数,这个状态直接计算起来很难搞。
可以枚举每一个位的111的数量,显然每一个位的111数量肯是偶数。然后用组合数转移即可。
然后设gig_igi表示m−zm-zm−z个数和不超过iii的方案数,那么有gi=∑j=0i(j+m−z−1m−z−1)g_i=\sum_{j=0}^i\binom{j+m-z-1}{m-z-1}gi=∑j=0i(m−z−1j+m−z−1),前缀和转移就好了。
然后答案就是(n+mm)−∑i=0nfign−i\binom{n+m}{m}-\sum_{i=0}^nf_ig_{n-i}(mn+m)−∑i=0nfign−i(注意这里的nnn已经减去mmm了),因为模数不是质数直接杨辉三角求就好了。
时间复杂度O(nmlogm)O(nm\log m)O(nmlogm),当然肯定是跑不满的
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2e5,P=1e9+9;
ll n,m,ans,c[N][51],f[N],g[N];
signed main()
{scanf("%lld%lld",&n,&m);ans=0;if(n<=m)return puts("0")&0;c[0][0]=1;for(ll i=1;i<=n;i++)for(ll j=0;j<=min(i,m);j++)c[i][j]=((j?c[i-1][j-1]:0)+c[i-1][j])%P;n-=m;ll z=(m+1)/2;f[0]=1;for(ll i=1;i<=18;i++)for(ll j=n;j>=0;j--)for(ll k=1;k<=z/2;k++){if(j<(k*(1<<i)))break;(f[j]+=f[j-k*(1<<i)]*c[z][2*k]%P)%=P;}for(ll i=0;i<=n;i++)g[i]=(g[i-1]+c[i+m-z-1][m-z-1])%P;for(ll i=0;i<=n;i++)(ans+=f[i]*g[n-i]%P)%=P;printf("%lld\n",(c[n+m][m]-ans+P)%P);return 0;
}