[DarkBZOJ3895]取石子

27 篇文章 0 订阅

题目

传送门 to DarkBZOJ

思路

在正常情况下,需要进行 ( n − 1 ) (n-1) (n1) 次合并和 ∑ a \sum a a 次取石子。那么这个数的奇偶性很可能就是答案。

仅剩的问题是,如果一堆石子是被取完的,那么合并次数就变少了。

简单思考一下就会发现,在 a i ⩾ 2 a_i\geqslant 2 ai2 时,不会出现这样的情况。将对手操作过的石子堆立刻与别的石子堆合并,就可以始终使得 a i ⩾ 2 a_i\geqslant 2 ai2

那么 a i = 1 a_i=1 ai=1 的石子堆(下文称为 “单石子”)该怎么处理呢?一般来说,可以考虑对称操作消除影响;然而本题中,合并操作是无法对称的!

我又考虑,去掉单石子(下文称为 “外面”)是先手必胜 o r or or 后手必胜时,有奇数 o r or or 偶数个单石子的情况。多数情况下 这是正确的;然而在本题中,这种分类方法太粗糙了!

不得已,只能手玩找规律。我手玩了单石子数目不超过 6 6 6 的所有情况,仍然没有发现任何规律;但是我突然想到——利用程序打表!于是我就把表打了出来,发现单石子数目对结果的影响是有循环节 6 6 6 的!

于是这题就做出来了。虽然没有任何道理。想有道理也可以,归纳法呗

我的手玩过程附在文末。这个过程启示我,只有四种情况:外面为空;外面只有一堆 a = 2 a=2 a=2 的石子;外面是先手必胜;外面是先手必败。

于是把四个情况的胜负性都存下来,就可以打表了。本来应当存在一个问题是,外面是先手必胜,操作后得到先手必败;外面是先手必败,操作后得到先手必胜;这是一个循环调用。但是我的手玩过程告诉我:应当不存在此种情况。

代码

#include <cstdio> // Dangerous Dark Ghost!!!
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
void writeint(unsigned x){
	if(x > 9) writeint(x/10);
	putchar(char((x%10)^48));
}

const int MAXN = 1005;
/// empty, only a = 2, win, lose
int e[MAXN], a2[MAXN], w[MAXN], l[MAXN];
int main(){
	e[0] = 0, a2[0] = 0, w[0] = 1, l[0] = 0;
	e[1] = a2[1] = w[1] = l[1] = 1;
	e[2] = 1; // just meld
	for(int i=2; i<=5; ++i){
		e[i+1] = !(a2[i-1] && e[i]);
		a2[i] = !(e[i+1] && a2[i-1] && w[i-1] && w[i-2]);
		w[i] = !(l[i-1] && w[i-1] && l[i-2]); // and l[i]
		l[i] = !(w[i] && w[i-1] && l[i-1] && w[i-2]);
		// printf("%d %d %d %d\n",e[i],a2[i],w[i],l[i]);
	}

	for(int T=readint(),ans; T; --T){
		int n = readint();
		int sum = -1, cnt = 0;
		for(int i=1,a; i<=n; ++i){
			a = readint();
			if(a == 1) ++ cnt;
			else sum += a+1;
		}
		if(cnt == n-1 && sum == 2) ans = a2[cnt%6];
		else if(cnt == n) ans = e[cnt%6];
		else if(sum&1) ans = w[cnt%6];
		else ans = l[cnt%6];
		puts(ans ? "YES" : "NO");
	}
	return 0;
}

同理 “外面” 是后手必胜,而 a i = 1 a_i=1 ai=1 的石子有奇数堆时,是先手必胜。

“外面” 是先手必胜呢?考虑 a i = 1 a_i=1 ai=1 的石子有奇数堆。可以用上面的对抗性策略,使得 first player \text{first player} first player 拿走最后一个 a i = 1 a_i=1 ai=1,然后 second player \text{second player} second player 就赢了。

后记

查了一下,网上还有 O ( n ∑ a ) \mathcal O(n\sum a) O(na) 的递推方法:用 f i , j f_{i,j} fi,j 表示 i i i 个单石子、外面需要进行 j j j 次操作时的胜负性。但是我有点疑惑……为啥外面的石子不能变成单石子呢?为啥外面的石子一定要完全合并呢?我不太懂。

手玩过程

一个单石子

先手必胜。如果外面是先手必胜,你就把单石子合并过去,得到先手必败;如果外面先手必败,直接将单石子拿走。

将单石子合并到外面 a n d and and 将其直接拿走,两种方案的胜负性不同。选择先手必败的即可。故 first player \text{first player} first player 必胜。

两个单石子
  1. 如果外面是空的,先手选择合并单石子,然后 first player \text{first player} first player 必胜。
  2. 外面是先手必胜,先手选择合并单石子,得到 零个单石子 の 后手必胜局面,则 first player \text{first player} first player 必胜。
  3. 外面是后手必胜(非空),则先手不能合并单石子、不能造出 一个单石子,只能在外面操作。如果外面满足 a i > 2 a_i>2 ai>2,在外面操作后,变成 ( 2 ) (2) (2) 中情况, second player \text{second player} second player 赢了。
  4. 如果外面存在 a i = 2 a_i=2 ai=2,那么操作它试试!后手肯定会考虑将 a i ′ = 1 a'_i=1 ai=1 立即合并走。如果外面有至少两堆,那么合并走之后就会得到 a j ′ ⩾ 3 a_j'\geqslant 3 aj3,则 second player \text{second player} second player 赢了。
  5. 如果外面仅有一堆 a i = 2 a_i=2 ai=2,操作之后变为 三个单石子 の 外面为空,这是先手必败的。所以 first player \text{first player} first player 必胜了!

总结:外面空则胜;外面仅 a i = 2 a_i=2 ai=2 则胜;否则与外面胜负情况相同。

三个单石子
  1. 外面为空,合并会得到 一个单石子,拿走就变为 两个单石子 の 外面为空,所以 second player \text{second player} second player 必胜。
  2. 外面非空,类比 一个单石子,要么合并一个单石子到外面,要么直接拿走,构造出 两个单石子 の 外面非空。除去 ( 3 ) (3) (3) 中的特例,都是 first player \text{first player} first player 必胜。
  3. 特例是,外面仅有一个 a i = 2 a_i=2 ai=2,此时上述两种方案都行不通。若将 a i = 2 a_i=2 ai=2 拿走一颗,变为 四个单石子 の 外面为空,这还是先手必胜。若合并两个单石子,则变为 一个单石子,更不行。故此时 second player \text{second player} second player 必胜了。

总结:外面空则败;外面仅 a i = 2 a_i=2 ai=2 则败;否则必胜。

四个单石子
  1. 外面为空,则 first player \text{first player} first player 胜。因为拿走一个就得到 三个单石子 の 外面为空。
  2. 外面是先手必胜,则先手合并单石子,得到 两个单石子 の 外面后手必胜(非空,非仅 a i = 2 a_i=2 ai=2 这堆),所以 first player \text{first player} first player 胜。
  3. 外面是后手必胜(非空),则先手不能合并单石子、一般不敢造出 三个单石子,只能在外面操作。如果外面满足 a i > 2 a_i>2 ai>2,显然 second player \text{second player} second player 必胜。
  4. 如果外面存在 a i = 2 a_i=2 ai=2,类比 两个单石子,也没啥用。当外面仅有一个 a i = 2 a_i=2 ai=2 时,造出 三个单石子 first player \text{first player} first player 就必胜了。

总结:外面空则胜;外面仅 a i = 2 a_i=2 ai=2 则胜;否则与外面胜负情况相同。

五个单石子
  1. 外面为空,则 first player \text{first player} first player 必胜了!因为合并后得到 三个单石子 の 外面仅 a i = 2 a_i=2 ai=2
  2. 外面非空,很可能 first player \text{first player} first player 必胜,原因跟 三个单石子 相同。特例见 ( 3 ) (3) (3)
  3. 特例是,外面仅有一个 a i = 2 a_i=2 ai=2 。变为 六个单石子 の 外面为空,则 first player \text{first player} first player 胜。

总结:必胜。跟 一个单石子 一样霸气。

六个单石子
  1. 外面为空,可以发现这是 second player \text{second player} second player 必胜的。
  2. 外面先手必胜,可以发现这是 first player \text{first player} first player 必胜的。
  3. 外面后手必胜(非空),一般情况下是 second player \text{second player} second player 必胜。
  4. 如果外面仅有一个 a i = 2 a_i=2 ai=2,还是 second player \text{second player} second player 必胜啊。

总结:外面空则败;否则与外面胜负情况相同。

七个单石子
  1. 外面为空,变为 六个单石子 の 外面为空,故 first player \text{first player} first player 必胜。
  2. ……(作者已经讨论到断气了)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值