Solution -「HNOI 2007」「洛谷 P3185」分裂游戏
Link.
给定 堆石子,数量为 ,双人博弈,每轮操作选定 ,使 ,,,并保证操作后所有 。求保证先手胜的第一步操作方案数和字典序最小的第一步操作。
多测,,,数据组数 。
由于每次只能取走一个石子,所以一个有 个石子的位置实际上可以看做 堆互不相关的石子放在同一个位置。而由于“互不相关”,求出每个位置上有一颗石子的 SG 函数异或起来就是答案。令 表示位置 有一颗石子的 SG 值,显然:
扫出 ,设所有石子 异或和为 ,据此判断是否有解。若有解,暴力枚举第一次操作的 ,若 ,说明操作后先手必败,此次操作计入贡献,最终 就解决啦!
/* Clearink */
#include <cstdio>
#include <cstring>
const int MAXN = 21;
int n, sg[MAXN + 5], a[MAXN + 5];
inline int calcSG ( const int i ) {
if ( ~sg[i] ) return sg[i];
bool vis[105] {};
for ( int j = i + 1; j <= n; ++ j ) {
for ( int k = j; k <= n; ++ k ) {
vis[calcSG ( j ) ^ calcSG ( k )] = true;
}
}
for ( int j = 0; ; ++ j ) if ( !vis[j] ) return sg[i] = j;
}
int main () {
int T;
for ( scanf ( "%d", &T ); T --; ) {
scanf ( "%d", &n ), memset ( sg, 0xff, sizeof sg );
int ans = 0;
for ( int i = 1; i <= n; ++ i ) {
if ( scanf ( "%d", &a[i] ), a[i] & 1 ) {
ans ^= calcSG ( i );
}
}
if ( !ans ) { puts ( "-1 -1 -1\n0" ); continue; }
int ways = 0;
for ( int i = 1; i <= n; ++ i ) {
if ( !a[i] ) continue;
for ( int j = i + 1; j <= n; ++ j ) {
for ( int k = j; k <= n; ++ k ) {
if ( !( ans ^ calcSG ( i ) ^ calcSG ( j ) ^ calcSG ( k ) ) && !ways ++ ) {
printf ( "%d %d %d\n", i - 1, j - 1, k - 1 );
}
}
}
}
printf ( "%d\n", ways );
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现