[SDOI2019] 移动金币
题意:
一个$1\times n$的棋盘上最初摆放有m枚金币。其中每一枚金币占据了一个独立的格子,任意一个格子内最多只有一枚金币。
Alice 和 Bob 将要进行如下的一场游戏:二人轮流操作,且 Alice 先行。
当轮到一个玩家的时候,他可以选择一枚金币,并将其向左移动任意多格,且至少移动一格。金币不能被移出棋盘,也不能越过其它金币。
如果轮到一个玩家的时候他已经无法做出任何有效操作了(显然这个时候m枚金币恰好落在最左侧的m个格子中),则被判定为输家。
已经知道 Alice 和 Bob 都是极致聪明的人,他们在任何局面下总能做出最优的操作。那么有多少初始状态能保证 Alice 必胜呢?
$n\leq 150000,m\leq 50$。
题解:
将两个金币之间的距离视作物品,那么显然这是一个阶梯博弈,问题变为求出奇数阶梯异或和不为0的方案数。
转换一下,相当于把$n-m$个东西分成$m/2$份使得$a_{1}\bigoplus a_{2}\bigoplus\cdots\bigoplus a_{m/2}=0$。
直接背包是$O(mn^{2})$的,但我们注意到二进制位之间互相不影响,所以可以考虑逐位背包。
设$dp(i,j)$表示考虑二进制最高的i位,已经用了j个东西的方案数。转移时枚举第i位有多少a的二进制位为1。
$dp(i,j)=\sum \limits_{k=0,k\%2=0}^{m/2}{C_{m/2}^{k}\times dp(i+1,j-k(1<<i))}$
考虑完奇数阶梯后,偶数阶梯的位置已经定了,而阶梯上物品个数未定,于是利用插板法计算答案。
$ans=\sum \limits_{i=0}^{n-m}{C_{i+m/2-1}^{m/2-1}\times dp(0,n-m-i)}$
复杂度$O(nm\log{n})$。
套路:
- 博弈论常见模型:巴什博弈,尼姆博弈,阶梯博弈,反尼姆博弈。
- 二进制位之间互不影响的计数$\rightarrow$按二进制位逐位dp。
代码:
#include<bits/stdc++.h> #define maxn 150005 #define maxm 55 #define inf 0x7fffffff #define mod 1000000009 #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; ll F[20][maxn],C[maxn][maxm]; inline ll read(){ ll x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline ll mo(ll x){return x>=mod?x-mod:x;} int main(){ int n=read(),m=read(),od=(m+1)/2,ov=m-od+1; if(n<=m){printf("0\n");return 0;} int d=0; while((1<<d)<=n) d++; F[d][0]=1,C[0][0]=1; for(rint i=1;i<=n;i++){ C[i][0]=1; for(rint j=1;j<=min(i,m);j++) C[i][j]=mo(C[i-1][j]+C[i-1][j-1]); } for(rint i=d-1;i>=0;i--) for(rint k=0;k<=od;k+=2) for(rint j=k*(1<<i);j<=n;j++) F[i][j]=mo(F[i][j]+C[od][k]*F[i+1][j-k*(1<<i)]%mod); ll ans=0; for(rint i=0;i<=n-m;i++) ans=mo(ans+C[i+ov-1][ov-1]*F[0][n-m-i]%mod); printf("%lld\n",mo(C[n][m]-ans+mod)); return 0; }