【洛谷P5675】取石子游戏
题目
题目链接:https://www.luogu.com.cn/problem/P5675
Alice 和 Bob 在玩一个古老的游戏。现在有若干堆石子,Alice 和 Bob 轮流取,每次可以选择其中某一堆的石子中取出任意颗石子,但不能不取,谁先取完使得另一个人不能取了算赢。
现在场地上有\(N\)堆石子,编号为 \(1\) 至 \(N\)。Alice 很快发现了这个游戏存在一些固定的策略。阴险的 Alice 想赢得这场比赛就来找到主办方你,希望你在这 \(N\) 堆石子中选出若干堆石子作为最后游戏用的石子堆并使得 Alice 能获得胜利。你自然不想让 Alice 得逞,所以你提出了一个条件:Alice 在这个游戏中第一次取的那堆石子的编号需要你来指定(仅指定取的石子堆编号,不指定第一次取多少个,这个指定的石子堆必然包含在最后游戏用的石子堆中)。
现在你很好奇,你想算算有多少种方案让 Alice 不能获胜。注意,即使选出的石子堆的编号的集合完全相同,指定第一次取的石子堆的编号不同,也认为方案是不同的。
思路
设 \(f[i][j]\) 表示前 \(i\) 个数字取了若干个异或和为 \(j\) 的方案数,\(g[i][j]\) 表示第 \(i\) 个数以后取若干个数异或和为 \(j\) 的方案数。
枚举每一个位置作为选择的石子堆,假设其他石子的异或和为 \(k\),那么我们只需要满足无论这堆石子取多少,都有 \(k \operatorname{xor}\) 取的石子数 \(\neq 0\)。也就是需要满足 \(k\geq a_i\)。
直接无脑 \(O(nA^2)\) 统计答案即可。其中 \(A\) 表示 \(a\) 的上限。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=260,MOD=1e9+7;
int n,ans,a[N],f[N][N],g[N][N];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
f[0][0]=g[n+1][0]=1;
for (int i=1;i<=n;i++)
for (int j=0;j<=255;j++)
f[i][j]=(f[i-1][j]+f[i-1][j^a[i]])%MOD;
for (int i=n;i>=1;i--)
for (int j=0;j<=255;j++)
g[i][j]=(g[i+1][j]+g[i+1][j^a[i]])%MOD;
for (int i=1;i<=n;i++)
for (int j=0;j<=255;j++)
for (int k=0;k<=255;k++)
if ((j^k)>=a[i])
ans=(ans+1LL*f[i-1][j]*g[i+1][k])%MOD;
printf("%d",ans);
return 0;
}