帆软杯武汉大学新生赛 I 犹太棋(博弈,SG函数)

题目链接

题意

"犹太棋"是一种经典的巴什博弈游戏,本题的游戏由其玩法改编而来。你并不需要了解关于"犹太棋"的知识,只需要仔细阅读以下的规则说明:

有一个长为 n ,宽为 1 的棋盘,现在给定一个初始局面,某些地方已经有了棋子, Alice 选手和 Bob 选手开始下棋,双方轮流行动, Alice 先手。

每一次,当前行动的人可以选择连续的一段、没有棋子的、长度不大于 3x个位置,将这些位置都放上一枚棋子,最先不能操作的人判负。

现在 Alice 想要知道,如果双方都足够聪明,她是否是先手必胜的?

如果是,输出YES;否则,输出NO

其中,0n3000

做法

比较显然是用SG函数做,已经放置了棋子的地方把棋盘分为了几个连续段,等价于把一个游戏分成了多个子游戏。
根据SG定理,总的游戏的SG函数值等于所有子游戏SG函数值的异或和。

·对于每次操作,可以取长度为1/2/3的连续一段放上棋子,相当于把连续的一段分为1/2段,也就是一个游戏分为1/2个子游戏。
分为1个长度为k的子游戏可以看作是分为一个长度为0的子游戏和一个长度为k的子游戏,所以每一次操作都可以看做把一个游戏分为两个子游戏。
根据SG定理,SGx=mex{S=(SGi Xor SGj)|i+j=xk,k=1/2/3}

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
int n; string s; cin >> n >> s;
vector<int> sg(n + 1, -1);
sg[0] = 0; sg[1] = 1; sg[2] = 2;
auto get = [&](auto self, auto &sg, auto x) {
if (sg[x] != -1) return sg[x];
set<int> s;
for (int i = 1;i <= 3;i++) {
for (int j = 0;j <= x - i;j++) {
if (x - i - j >= 0)
s.insert(self(self, sg, j) ^ self(self, sg, x - i - j));
}
}
for (int i = 0; i < n + 2;i++) {
if (s.find(i) == s.end()) {
sg[x] = i;
break;
}
}
return sg[x];
};
int ans = 0;
for (int i = 0;i < n;i++) {
if (s[i] == '0') {
int j = i;
while (j < n - 1 and s[j + 1] == '0') j ++;
int len = j - i + 1;
ans ^= get(get, sg, len);
i = j;
}
}
cout << (ans ? "YES\n" : "NO\n");
}
signed main(void) {
cin.tie(nullptr), cout.tie(nullptr) -> ios::sync_with_stdio(false);
solve();
return 0;
}
posted @   Coldarra  阅读(187)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示