盖世计划-0724-B班模拟 C. 游戏 (game)
首先,Alice 先去
个商店中购买物品。其中第 个商店售卖编号为 的物品,且每个物品的售价为 。Alice 的总花费不能超过 。 接着,Bob 再去另外
个商店中购买物品。其中第 个商店售卖编号为 的物品,且每个物品的售价为 。Bob 的总花费不能超过 。 然后,Alice 和 Bob 将他们买到的物品放在一起。然后从 Alice 开始,他们轮流进行如下操作:
- 从剩余的物品中取走若干个物品(至少一个),取走的物品必须编号相同。
最后无法操作的人输。问 Alice 最初有多少种购买方式,使得 Alice 在后续的游戏中有必胜策略。Alice 的两种购买方式不同当且仅当两种方案中 Alice 在某一个商店中购买的物品的数量不同。答案对
取模。
, 。
一看,这游戏不就是 nim 游戏嘛,有结论:
设有
堆石子,第 堆石子数量为 ,当且仅当 不为 时先手必胜。
分析题目的限制,第一个限制可以写成
我们假如 Alice 取出的石子为
所以分析完,限制就是
一看新题,又不会了。
- 进位
- 与上界的大小关系情况
第一个限制就变成 01 背包问题了,我们对这一位求背包就可以在
第二个限制关心的实际上就是背包取数的数量奇偶性吧,所以背包的状态加一位表示目前取物数量的奇偶性即可。
设
这里的枚举顺序为从低位到高位,与数位 dp 不同,有时这样的状态更加方便。
时间复杂度为
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back
using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*
*/
const int N = 110, mod = 1e9 + 7;
int n;
int a[N], s;
i64 k, m;
i64 f[2][N * N][2][2], g[N * N * 2][2];
bool comp(int x, int y, int z) {
if(x > y) return 1;
if(x == y && z) return 1;
return 0; //比较数组
}
int main() {
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> k >> m;
for(int i = 1; i <= n; i++) {
std::cin >> a[i];
s += a[i];
}
int cur = 1, lst = 0; //滚动数组
f[cur][0][0][0] = 1; //初始化
for(int b = 0; b < 60; b++) { //按位 dp
std::swap(cur, lst);
for(int kl = 0; kl < 2; kl++) { //枚举先前与 k 的大小关系
for(int ml = 0; ml < 2; ml++) { //枚举先前与 m 的大小关系
for(int i = 0; i <= s; i++) g[i][0] = f[lst][i][kl][ml], f[lst][i][kl][ml] = 0; //背包初始化,继承先前的进位贡献
int nws = s; //先前的进位上界
for(int i = 1; i <= n; i++) {
nws += a[i];
for(int j = nws; j >= 0; j--) {
for(int k = 0; k < 2; k++) { //枚举取的数量的奇偶性
if(g[j - a[i]][k ^ 1] && j - a[i] >= 0) g[j][k] = (g[j][k] + g[j - a[i]][k ^ 1]) % mod; //背包
}
}
}
for(int i = 0; i <= (s << 1); i++) { //枚举进位,注意是两倍,形如 1+1/2+1/4+1/8...<=2
for(int j = 0; j < 2; j++) { //枚举异或后这一位是 0/1
int x = comp(i & 1, ((k >> b) & 1), kl), y = comp(j, ((m >> b) & 1), ml); //判断大小
f[cur][i >> 1][x][y] = (f[cur][i >> 1][x][y] + g[i][j]) % mod; //转移,到下一位时进位/2
g[i][j] = 0; //清空
}
}
}
}
}
std::cout << f[cur][0][0][1] << "\n"; //答案,表示进位为 0,sum(ac)<=k且xorsum(c)>m的方案数
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具