比赛的时候没想明白,而且最后3人开3题过度自信,导致rank惨烈,才拍18名……刷新第二底线了……赛后和队友讨论了一下,发现自己看错题意了,纠正以后推导一番顺利的推倒了此题……sign,每次我都要至少读错题一次……什么水平。
题目是这样的,给你n堆石子,每次丢掉若干个,剩下k个要求 0 < k < n(要求1),而且要加上一堆新的n^k个石子,且满足n^k < n(要求2)。显然一个游戏局面n被分解为2个游戏局面k, n^k。所以又有SG(n) = SG(k) ^ SG(k * n)。还有一种操作是分解为k, n ^ (k << 1),但是只能用一次(要求3)。
首先SG(2^n) == 0这是显然的,因为他不能再分解了。
我们设countbit(n)等于n二进制中1的个数。
然后我们看countbit(n)奇偶性,他等于countbit(k) +countbit(n ^ k)的奇偶性,这点可以从每一位来考虑。
看xor的真值表如下
n\k | 0 | 1 |
0 | 0 | 1 |
1 | 1 | 0 |
从xor运算的真值表,我们可以发现上诉性质成立。
又从最开始的定义我们发现,以及SG(2^n) == 0,我们可以推导发现SG(n) = (countbit(n) & 1) == 0。
证明如下:
如果countbit(n)为偶数,由于分解方法必定收敛(要求1,要求2以及要求3决定)最终必定分解整成偶数个2^x堆石子的形式,而每次分解新加一堆,所以一共分解奇数次,所以先手必胜,而且无论怎么分解后手都会失败(又最终收敛性,最终堆数为奇数决定),所以所有后续状态SG和为0,所以SG(n) = 1。
countbit(n)为奇数的同理可以证明,必然分成奇数个2^x堆石子的形式,必然是偶数堆,如论先手怎么分解先手必败,所以SG(n) = 0;
有了这个性质我们就可以发现n ^ (k << 1)这个操作完全是一个幌子,因为无论左移几位都无关,只与countbit(n)的奇偶性有关,只可以用一次就是表示一定有终止状态,否则可能无法终止。
所以同一切nim游戏一样,求出每堆石子的SG函数,然后用NIM和,求解即可,标程里所谓的N + K (K = sum(countbit(i)))也就是这个道理吧。
暴力跑SG的程序如下:
1 int sg[N]; 2 int countbit(int x) { 3 int res = 0; 4 while(x) { 5 res++; 6 x &= (x - 1); 7 } 8 return res; 9 } 10 int SG(int x) { 11 if(sg[x] != -1) { 12 return sg[x]; 13 } 14 vector<int> mex; 15 for(int i = 1;i < x;i++) { 16 if((x ^ i) < x) { 17 mex.push_back(SG(x ^ i) ^ SG(i)); 18 int cnt = countbit(x); 19 int oth = countbit(x ^ i) + countbit(i); 20 int lsf = countbit(x ^ (i << 1)) + countbit(i); 21 assert((cnt & 1) == (lsf & 1)); 22 assert((cnt & 1) == (oth & 1)); 23 } 24 } 25 int s = 0; 26 for(int i = 0;i < int(mex.size()) && s == mex[i];i++, s++); 27 return sg[x] = s; 28 }
题目代码如下:
1 #pragma comment(linker, "/STACK:1024000000,1024000000") 2 #include <iostream> 3 #include <cassert> 4 #include <cstdio> 5 #include <cstring> 6 #include <cmath> 7 #include <algorithm> 8 #include <vector> 9 10 using namespace std; 11 12 int countbit(int x) { 13 int res = 0; 14 while(x) { 15 res++; 16 x &= (x - 1); 17 } 18 return res; 19 } 20 int main() { 21 int n, t, v; 22 cin >> t; 23 for(int cases = 1;cases <= t;cases++) { 24 int sg = 0; 25 cin >> n; 26 for(int i = 0;i < n;i++) { 27 cin >> v; 28 sg ^= (countbit(v) & 1) == 0; 29 } 30 printf("Case %d: %s\n", cases, sg ? "Yes" : "No"); 31 } 32 return 0; 33 }