Boapath

   :: 首页  :: 新随笔  ::  ::  :: 管理
 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<string.h>
 4 #include<map>
 5 #include<string>
 6 using namespace std;
 7 map<string,int>vis;
 8 bool obious(int x){
 9     int cnt = 0;
10     while(x>0){
11         if(x%2)cnt++;
12         x /= 2;
13     }
14     if(cnt%2) return true;
15     return false;
16 }
17 int string_int(string x){
18     int ans = 0;
19     for(int i = 0;i<x.size();i++){
20         ans = ans*10+x[i]-'0';
21     }
22 //    cout<<"ans:"<<ans<<endl;
23     return ans;
24 }
25 int main()
26 {
27 #ifdef LOCALL
28 //    freopen("in.txt","r",stdin);
29     //freopen("out2.txt","w",stdout);
30 #endif
31     int n;
32     while(cin>>n) {
33         int ans  = 0;
34         vis.clear();
35         if(n == 0){cout<<"Yes\n";continue;}
36         for(int i = 0;i<n;i++){
37             string xx;
38             cin>>xx;
39             if(vis.count(xx))continue;
40             vis[xx] = 1;
41             int x = string_int(xx);
42 //            cout<<"x:"<<x<<"obious:"<<obious(x)<<endl;
43             if(obious(x)){
44                 ans^=2*x;
45             }
46             else ans ^= (2*x+1);
47         }
48         if(ans)cout<<"No\n";
49         else cout<<"Yes\n";
50     }
51     return 0;
52 }
HDU3537

幸好先看了Game Theory,要不然又要哭着找解题报告了……

是Mock Turtles:这类游戏每次可以翻动一个、二个或三个硬币。

本来打表SG也是可以的,可是论文里最精髓的部分在于找了这个SG表的规律。

因为这个规律不像之前那些题目那样那么明显。有兴趣的可以自己先找找规律。

规律是什么呢?

先来看sg表:

Position x 0 1 2 3 4 5 6 7 8 9 10 11 12 13
sg[x] 1 2 4 7 8 11 13 14 16 19 21 22 25 26

 

也许有些同学不清楚这些sg[x]是怎么得来的,那么我再唠叨一下:

  比方说sg[2] : 因为每次可以翻动一个、二个或三个硬币,现在第2个位置是正面(一定要翻的),0,1号位置都是反面,所以下一步可以翻到的局面有:反反反(终态,P,sg = 0),正反反(sg = sg[0] = 1),反正反(sg = sg[1] = 2),正正反。

  现在“正正反”的sg是多少呢?

  还记得我们一开始说的(额……在平等博弈那篇里),关于翻硬币的问题,我们可以化为每个正面硬币单独存在的局势的异或和,所以正正反 = 正 + 反正 = sg[0]+sg[1] = 3(这里的加是二进制加,就是异或)。

  根据sg函数的性质,我们知道最小没有出现的自然数就是4了,所以sg[2] = 4。

 

  再分析个sg[3]的:

  0 0 0 1 可以变为 0 0 0 0( sg = 0), 1 0 0 0(sg[0] = 1) ,0 1 0 0(sg[1] = 2), 0 0 1 0(sg[2] = 4) , 1 1 0 0(sg[0]^sg[1] = 3), 0 1 1 0(sg[1]^sg[2] = 6),1 0 1 0(sg[0]^sg[2] = 5).

  所以sg[3] = 7.

   以上。

 

然后,现在我们知道了SG表(嘛……之后说说怎么计算机打表的,人工打累死……)

然后最精彩的部分来了:

  我们先定义2个代名词:

        1.  odious:二进制表示中1的个数为奇数

        2.  evil: 二进制表示中1的个数为偶数

  结论是:

    if(2x) == odious,sg(x) = 2x;

    if(2x) == evil,sg(x) = 2x+1.

  而且sg值是odious数字的连续升序(额,不知道怎么准确描述,自创词汇,就是说sg[x]和sg[x-1]之间没有其他的odious数了)。

     额……证明基于一个事实(为什么是事实,反证一下就清楚了):(这个事实重要啊!这是证明的核心!

                                                      evil^evil=odious^odious=evil

                                                      evil^odious=odious^evil=odious

  + 数学归纳法。

  证明:

  目标:证明sg值是odious数字的连续升序。

  具体实现(不严谨版):

    1、假设在位置x左边的所有位置的sg值满足这个性质

    2、目标转变:企图证明,x位置的sg值就是下一个obious数。

        首先,根据规则,x这个位置的硬币肯定要翻过来。

      然后如果我们只翻这一位,0;——也就是说0这个数被排除,不会是sg[x](事实由于0是evil数,所以没什么关系)。

      如果翻2位,也就是说变成了翻x左边的一位(因为x一定要被翻成反面)——把左边一位的反面翻成正面,就成了sg[x-i]{x=>i>0},而由1知,这个数sg[x-i]一定是一个odious数(而且是出现过的,就是说不会是下一位)。然后i是从1到x,所以,之前所有的odious数都会出现过。

        如果翻3为,同理,变成了2个odious数,而odious^odious=evil,所以不会影响下一个odious数的出现。

    综上得证。(PS:有些同学可能会问,为什么“之前所有的odious数都会出现”,现在出现的就一定是下一个odious数,下2个,下3个不行么?答案是不行的,为什么呢?不要忘了我们关于SG函数的定义,SG函数定义的时候就说了“最小没有出现的(自然)数”,如果是现在的sg[x]是“下2个”,而且又满足上面2的推理,那就说明“下一个”还没有出现,那sg[x]应该是“下1个”的值才对,这和假设的“sg[x]是“下2个””矛盾。所以一定是“下一个”……)

    

MT与NIM:

  我看论文的时候发现其实这个Mock Turtles很像Multi-SG(就是可以选择把1堆分成2堆的那个)。

  MT可以允许每次翻1、2、3个。

  只翻1个,就到终态,就是nim里的,取一堆,全部取完了。

  只翻2个,就是nim里的取一堆,但是没取完。

  只翻3个,就是nim里的,选一堆,不取石子,而是把这堆分成2个小堆。——当然,这两个小堆怎么分,就不是按照平时的整数拆成2个整数相加那么直接了。

 

最后说说怎么打表吧,打个小点的表,那就按照我上面说的与NIM的类比进行DFS打表吧……(就说说……给个方向……

 

番外:

  窝: 可是证明好像还是没有说为什么

    “if(2x) == odious,sg(x) = 2x;

    if(2x) == evil,sg(x) = 2x+1.”

    是正确的吧!

  Big窝:嘛…… Think!骚年!

  窝:   可是那个证明只说明了SG函数的值是odious数的……额……连续……升序啊……

  Big窝:为什么要考虑2x?除了找到规律之外,当我们知道结论之后,能知道为什么吗?

  窝: 恩……我想是因为odious数的定义吧!奇数个1。……一个自然数写成2进制,其中1的个数不是奇数,就是偶数,我想所以才会是和2x有关的规律吧。

  Big窝: 然后?

  窝: 然后?!然后就求2x是不是odious数!如果不是,+1肯定是的!

  Big窝:……这样就证明了?

  窝:(点头)

  Big窝:嘛……你说“2x是不是odious数!如果不是,+1肯定是的!”……我觉得你说的最大的漏洞在于,我们关于‘SG函数的值是odious数的连续升序“本来就是

   “if(2x) == odious,sg(x) = 2x;

    if(2x) == evil,sg(x) = 2x+1.”

  这个结论推导出来的,现在你用这个结论去证明它的前提,你觉得正确性如何?

  窝: ……

  Big窝:不过你说的想法,可以作为以后思考后猜想的方向,嘛……也不是完全没有用的……

  窝:……

  

posted on 2014-01-19 11:55  Boapath  阅读(544)  评论(0编辑  收藏  举报