题目描述
给定一棵树,树上每一个节点都有一枚棋子。
Alice 和 Bob 在这棵树上玩一个这样子的游戏 :
Alice 和 Bob 轮流操作,每次把一枚棋子移动到其子树内的节点(显然一枚棋子在叶子节点就不能移动了),不能移动的人算输。
问 Alice 先手是否有必胜策略,若先手必胜,则求出第一步移动棋子的方案有多少种。
正解
这里先介绍一下 SG 函数(其实只需要知道结论就行)。
一个状态的 SG 函数值(后面简称 SG 值)是其所有后继状态(进行一次操作后所到达的状态) ---手动换行---
SG值 集合的 (最小不属于这个集合的非负整数,比如说 )。
形式化的来说 。
结论 :
若当前局面的 SG 值为 0,先手必败,否则先手必胜。
若一个游戏是多个独立游戏组成的,那么当前局面的 SG 值是多个独立游戏 SG 值的异或和 (就是 C++ 里面的 ^ 运算符)。
正题 :
对于这道题,对于每一枚棋子其实都是独立的。
也就是说,当前局面的多个独立游戏就是每一枚棋子,当前局面的 SG 值就是每一枚棋子的 SG 值的异或和。
那么一枚棋子的 SG 值怎么求呢?
若一枚棋子在叶子节点(后继状态是空集),它的 SG 值为 0。
若一枚棋子的后继节点只有叶子节点,它的 SG 值为 1。
若一枚棋子的后继节点 SG 值最大为 1 (那么肯定也有 0),它的 SG 值为 2。
若一枚棋子的后继节点 SG 值最大为 2 (那么肯定也有 1, 0),它的 SG 值为 3。
若一枚棋子的往子树内最多可以走 步,它的 SG 值为 。
可以通过一遍 dfs 得到所有棋子的 SG 值,进而得到当前局面的 SG 值,从而判断胜负。
那么怎么求方案呢?
注意到,只有 SG 值为 0 的局面是必败的,也就是说,第一次移动之后的局面 SG 值一定要等于 0。
考虑当前要移动的棋子为 ,它的 SG 值为 ,且令移动之前局面的 SG 值为 。
要使移动后 SG 值为 0,这枚棋子需要移动到 SG 值等于 的局面。
现在只需要求出树上每一个点子树内 SG 值的一个桶,就可以求出答案了。
具体怎么想怎么求就怎么求呗。
可以直接树差分或者长剖做到 ,亦或者用重剖做到 。
代码
这里安利一下 CYJian 的树差分代码。
然后还有我的辣鸡长剖代码。