SPOJ 2154 Kruskal

SPOJ 2154 Kruskal
【题目大意】
  猴子和Kruskal玩一个取石子游戏,给定n堆石子,n不大于200,每堆石子的个数大于2小于2 ^ 32,双方轮流取子,每次可以从一堆中取最多k个,当一方取完石子后某堆石子的个数是素数的话那么当前玩家获胜。问猴子是否有必胜策略。

【题目分析】
  这是一道BT题,中间设置了许多trick。开始对题意没有完全理解,错了很多次,后来找来了数据,才发现了问题。
  题目中描述的获胜策略是:"A player wins if after his move the size of some heap is a prime number."这句话乍一看以为是取完石子后剩下的石子个数是素数的时候就获胜,其实还隐藏着另一种可能:如果多堆石子个数是素数,当前玩家无论怎样取都能获胜,因为在他取完之后,其他堆的石子个数是素数,也满足获胜条件。
  接下来考虑一般情况。这个题目是限制中间状态的Nim游戏,也就是说,对于一堆个数为n的石子而言,它的SG值取决于小于n的最大素数。注意这里题设又有了一个小trick,题目说明了需要取1到k个,如果当前石子个数本身是素数,当然是没用的,因此是小于n的最大素数。设小于n的最大素数是p(题目中说明了石子个数大于2,因此p一定存在),那么可以在k步以内到达p的一定是必胜态,而且是直接获胜,需要在输入的时候特判(这一点需要注意,在解决限制中间状态的Nim游戏时一般都需要特判)。然后就是p + k + 1这个状态,因为它可达的状态全是必胜态,因此它是必败态,SG值为0。现在考虑大于p + k + 1的状态,问题出来了。以p + k + 2这个状态为例,因为它可以到达p + 2 ... p + k + 1这些状态,因为p + 2 ... p + k都是直接获胜状态,如何判定他们的SG值呢?如果假设它们的SG值是1,那么p + k + 2这个状态的SG值应该是2。但是思考一下SG值的定义,它是定义在一个DAG上的,所有的状态最后都是可以在有限步内转移到终止态(必败态)。但是p + 1 ... p + k这些状态都转移到了p这个状态上,我们肯定不能认定p状态是终止态,因此仅仅凭借p + 1 ... p + k这些状态是必胜态就简单的把它们的SG值设为1是不恰当的;这些限制状态和以前的题目还不同,这些限制状态都不能转移到终止态上,但是由于题目的要求,它们又都是必胜态,因此把它们的SG值设为无穷大更合适些。
  仔细思考一下带限制状态的SG游戏,可以发现,它们和一般的SG游戏的区别在于,在分析一般的SG游戏的时候,对于一个状态图而言,转移到终止态的时候并不意味着游戏结束,因为玩家可以通过走其他的状态图来保证是否达到必胜态;但是带限制状态的SG游戏,限制的状态双方都是不敢走的,因为一旦一方走入限制状态,另一方立刻获胜,游戏就终止了。可以认为,只要走入限制态就相当于认输,对于双方玩家而言肯定都不会这样做,因此这些限制状态就成了“死状态”,完全可以忽视这些状态(也就是说不存在到这些状态的转移)。通过上述分析,我们可以认定p + k + 2这个状态的SG值应该是1而不是2。
  现在这个问题的做法就比较明朗了,对于每堆石子,去掉限制态的讨论后,就变成了在集合{1...k}中选择元素的一个Subtraction Game,它的SG值是模(k + 1)循环的。
  然后就是求最近素数的问题了,这个没有好办法,只能暴力枚举。对于一个数n,在sqrt(n)到n之间存在素数(感觉应该是,但是不会证),因此最多枚举sqrt(n)次就能找到解。但是每次枚举判素的复杂度还是sqrt(n),总复杂度还是比较高,我在本地跑数据跑了3秒,交上去超时了(SPOJ的时限10秒,这居然也超时,数据够变态)。想了很久也没有什么新的算法,无奈只能在判素上下点功夫,直接把Miller-Rabin搞出来了,速度提高到了2秒,仍然超时。后来又加了一些常数上的优化,终于过了。

【总结】
  这个题目做了很长时间,一方面是审题不清,错了几次;另一方面是对于SG的理论理解的不够透彻,想了很久终于想明白了;再有就是优化算法也花了很长时间。不过通过这个题目对于SG理论的理解又进了一步,感觉不错,呵呵。

注:本文作于2009年8月6日 9点27分

你可能感兴趣的:(SPOJ 2154 Kruskal)