Live2D

Solution -「HNOI 2007」「洛谷 P3185」分裂游戏

Description

  Link.

  给定 n 堆石子,数量为 {an},双人博弈,每轮操作选定 i<jk,使 aiai1ajaj+1akak+1,并保证操作后所有 ai0。求保证先手胜的第一步操作方案数和字典序最小的第一步操作。

  多测,n210ai104,数据组数 10

Solution

  由于每次只能取走一个石子,所以一个有 x 个石子的位置实际上可以看做 x 堆互不相关的石子放在同一个位置。而由于“互不相关”,求出每个位置上有一颗石子的 SG 函数异或起来就是答案。令 sg(i) 表示位置 i 有一颗石子的 SG 值,显然:

sg(i)=mexi<jk{sg(j)sg(k)}

  扫出 sg,设所有石子 sg 异或和为 X,据此判断是否有解。若有解,暴力枚举第一次操作的 i,j,k,若 Xsg(i)sg(j)sg(k)=0,说明操作后先手必败,此次操作计入贡献,最终 O(Tn3) 就解决啦!

Code

/* 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;
}
posted @   Rainybunny  阅读(119)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示