2012 Multi-University Training Contest 9 1009解题报告 HDU 4388

比赛的时候没想明白,而且最后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\k01
001
110

从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的程序如下:

 

HDU 4388 暴力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 }

 

题目代码如下:

HDU 4388
 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 }

 

转载于:https://www.cnblogs.com/ronaflx/archive/2012/08/21/2649880.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值