[ZJOI2009]染色游戏

27 篇文章 0 订阅

题目

传送门 to DarkBZOJ

思路

这类问题的通用转化:一个硬币被翻转,等价于另外开一个游戏,这个游戏中最初只有该硬币是可翻转的。以后的博弈就包含这两个游戏,但是一次只能操作一个游戏。

如果你学过非平等博弈(而且不像我一样是学过但是根本没听懂的),那么用 ( G + G ) ∥ 0 (G+G)\parallel 0 (G+G)0 就得到了证明。如果没有,可以用博弈的角度去理解。在 G G G 博弈(包含多个游戏)中,如果一个硬币的反面向上次数是偶数,那么考虑将这个硬币等效为正面朝上(无法操作)后,得到的 G ′ G' G 博弈:

  • 如果 G ′ G' G 先手必胜,那么 G G G 先手必胜。先将多个游戏中的该硬币反面向上情况两两匹配,然后 second player \text{second player} second player 操作一个游戏中该硬币时, first player \text{first player} first player 就在匹配的游戏中模仿操作。故 first player \text{first player} first player 必胜。
  • 如果 G ′ G' G 后手必胜,那么 G G G 后手必胜。还是两两匹配、对称操作。

同理,如果硬币反面向上次数是奇数,那么只需要保留一枚该硬币,剩下的就可以两两匹配、对称操作。

上面的证明是比较愚蠢的。重要的是我们要使用 s g sg sg 函数(参见我的博客)。手玩一下,你会发现表格形如
1 2 1 4 1 2 1 8 2 4 8 16 32 1 8 16 32 \begin{matrix} 1&2&1&4&1&2&1&8 \\ 2&4&8&16&\colorbox{pink}{32}\\ 1&8&16&32 \end{matrix} 121248181641632132218
由于表格是沿对角线对称的,我略去了一些。如果补全,不难发现
{ sg ⁡ ( 0 , x ) = lowbit ⁡ ( x + 1 ) sg ⁡ ( x , y ) = 2 x + y ( x y ≠ 0 ) \begin{cases} \operatorname{sg}(0,x)=\operatorname{lowbit}(x{\rm+}1)\\ \operatorname{sg}(x,y)=2^{x+y} & (xy\ne 0) \end{cases} {sg(0,x)=lowbit(x+1)sg(x,y)=2x+y(xy=0)
其中行和列都从 0 0 0 开始编号。 lowbit ( n ) \text{lowbit}(n) lowbit(n) n n n 的最低二进制位的值。发现规律之后,其实很容易证明,毕竟手玩的过程已经带给了你许多启示。

sg ⁡ ( 0 , x ) \operatorname{sg}(0,x) sg(0,x) 的取值很容易验证。手玩时,我们发现,目前得到的 max ⁡ \max max 是不可逾越的。所以我们可以这样想:假设 x + 1 ∈ [ 1 , 2 k ) x+1\in[1,2^k) x+1[1,2k) 已经成立。那么,对于 x + 1 ∈ [ 2 k , 2 k + 1 ) x+1\in[2^k,2^{k+1}) x+1[2k,2k+1) 时,其后继状态的连通块一旦包含 x + 1 = 2 k x+1=2^k x+1=2k,就一定不小于 2 k 2^k 2k 了(因为这是目前的全局最大值,别人的 s g sg sg 值都够不到第 k k k 个二进制位)。所以事实上 x + 1 x+1 x+1 的处境和 ( x − 2 k ) + 1 (x-2^k)+1 (x2k)+1 完全相同。而 x + 1 = 2 k + 1 x+1=2^{k+1} x+1=2k+1 时,恰好 sg ⁡ ( 0 , x ) = 2 k + 1 \operatorname{sg}(0,x)=2^{k+1} sg(0,x)=2k+1 。翻译一下,这就是 lowbit ⁡ \operatorname{lowbit} lowbit 啊!

其次,如果 sg ⁡ ( x − 1 , y ) = sg ⁡ ( x , y − 1 ) = 2 k \operatorname{sg}(x{\rm-}1,y)=\operatorname{sg}(x,y{\rm-}1)=2^k sg(x1,y)=sg(x,y1)=2k,那么 sg ⁡ ( x , y ) = 2 k + 1 \operatorname{sg}(x,y)=2^{k+1} sg(x,y)=2k+1 。原因很简单,根据定义,含 ( x − 1 , y ) (x{\rm-}1,y) (x1,y) 的连通块去掉 ( x − 1 , y ) (x{\rm-}1,y) (x1,y) 之后,得到的 s g sg sg 值取遍 [ 0 , 2 k ) [0,2^k) [0,2k) 。那么加上 ( x − 1 , y ) (x{\rm-}1,y) (x1,y) ( x , y − 1 ) (x,y{\rm-}1) (x,y1) 后, s g sg sg 值不变,却成为了 ( x , y ) (x,y) (x,y) 的合法连通块。再去掉 ( x , y − 1 ) (x,y{\rm-}1) (x,y1),就取遍了 [ 2 k , 2 k + 1 ) [2^k,2^{k+1}) [2k,2k+1) 。根据归纳法,左上角不存在达到 2 k + 1 2^{k+1} 2k+1 s g sg sg 值,所以当前 s g sg sg 值就是 2 k + 1 2^{k+1} 2k+1 了。

最后,由于上一个自然段证明的结论,说明 sg ⁡ ( 1 , x ) \operatorname{sg}(1,x) sg(1,x) 的取值合理即可。以那个 pink \colorbox{pink}{pink} pink 32 32 32 为例。你会发现它左边已经有了 1 , 2 , 4 , 8 , 16 1,2,4,8,16 1,2,4,8,16 任你选用!所以我们直接要求最顶上一排,除了最左边的 1 1 1,都被选入连通块。然后 1 , 2 , 4 , 8 , 16 1,2,4,8,16 1,2,4,8,16 都可以任取——唯一的特例是选 2 2 2 且不选 1 , 4 1,4 1,4,此时不是连通块。但是我们发现,最上面一排的最左端那个 2 2 2 是被选中了的!于是将这两个 2 2 2 同时删去即可。没有特例了, 1 , 2 , 4 , 8 , 16 1,2,4,8,16 1,2,4,8,16 当然可以异或出 [ 0 , 32 ) [0,32) [0,32) 所有数。

于是本题 O ( ∑ n m ) \mathcal O(\sum nm) O(nm) 的做完了。

代码

#include <cstdio> // Who's the principal?
#include <iostream> // He's just amongst us.
#include <algorithm>
#include <cstring> // A man in a case
#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 = 105;
int a[MAXN<<1], logtwo[MAXN];
char str[MAXN];

int main(){
	for(int i=0; (1<<i)<MAXN; ++i)
		logtwo[1<<i] = i; // pre-compute
	for(int T=readint(); T; --T){
		int n = readint(), m = readint();
		memset(a,0,(n+m)<<2);
		for(int i=0; i!=n; ++i){
			scanf("%s",str);
			rep(j,0,m-1) if(str[j] == 'T'){
				if(!i || !j){
					const int p = (i^j)+1;
					a[logtwo[p&(-p)]] ^= 1;
				}
				else a[i+j] ^= 1; // 2^{i+j}
			}
		}
		bool bad = false;
		for(int i=0; i!=n+m; ++i)
			if(a[i]) bad = true;
		puts(bad ? "-_-" : "=_=");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值