第十三课:容斥原理、简单博弈论

目录

一、容斥原理

 例题

 二、简单博弈论(Nim)

 (1)Nim游戏

习题:台阶Nim游戏(考虑构成胜败态的因素)

(2)集合—Nim游戏

 mex运算

sg函数

 必胜/败状态

习题:拆分—Nim游戏



一、容斥原理

637c27e5daac633b20143bb89847e883.jpeg

 容斥原理高中基本有涉猎,本身不再细讲,直接给出上图中四圆覆盖面积:

gif.latex?S%3DA+B+C+D-A%5Ccap%20B-A%5Ccap%20C-A%5Ccap%20D-B%5Ccap%20C-B%5Ccap%20D-C%5Ccap%20D+A%5Ccap%20B%5Ccap%20C+A%5Ccap%20B%5Ccap%20D+A%5Ccap%20C%5Ccap%20D+B%5Ccap%20C%5Ccap%20D-A%5Ccap%20B%5Ccap%20C%5Ccap%20D

 例题

e95f938582914f68ab56d030632a7d2b.png

分析:1.运用容斥原理计算。2.为了使用容斥原理,需要搞清楚如何遍历所有项。

容斥原理结果中,项数为:

gif.latex?C_%7Bn%7D%5E%7B1%7D+C_%7Bn%7D%5E%7B2%7D+...+C_%7Bn%7D%5E%7Bn%7D%3D%281+1%29%5E%7Bn%7D-C_%7Bn%7D%5E%7B0%7D%3D2%5En-1

 其中,组合数上标为奇数时,该项为正,为偶数时则为负。因此我们可以用一个m位二进制数表示。第i位表示该项包不包括第i个质数。例:0101表示gif.latex?-%283%5Ccap%201%29

由求解高精度组合数中我们学到,1~n中p的倍数的个数为:gif.latex?cnt%3D%5B%5Cfrac%7Bn%7D%7Bp%7D%5D。至此,由上述知识基础该题易解,代码如下:

//这里填你的代码^^
#include<iostream>
using namespace std;

typedef long long LL;
const int N =20;
int n,m;
int p[N];


int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
        cin>>p[i];

    int res=0;
    for(int i=1;i< 1<<m;i++)//遍历所有情况,,2的m次方
    {
        int cnt=0,t=1;
        for(int j=0;j<m;j++)
        {
            if(i>>j&1)
            {
                if((LL)t*p[j]>n)//必不可能整除n
                {
                    t=-1;
                    break;
                }

                t*=p[j];
                cnt++;
            }
        }
        if(t!=-1)
        {
            if(cnt%2) res+=n/t; //如果计算了奇数个集合
            else res-=n/t;
        }
    }
    cout<<res;
    return 0;

}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3782804/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 二、简单博弈论(Nim)

先解释两个名词:

1.必胜态:总有办法走一步,使得局面变为必败态。

2.必败态:无论怎么走,都会使得局面变为必胜态。

 (1)Nim游戏

9d5995f3a0014b8aa5b3b6c43fe40132.png

 针对此类游戏,我们设每堆石子中石子数为a(i),终局时石子数全为0,为必败态。

首先给出结论,必败态为:

gif.latex?a_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20a_%7B3%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bn%7D%3D0

必胜态为:

gif.latex?a_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20a_%7B3%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bn%7D%5Cneq%200

证明:

  先将每个石子数写成二进制形式。首先证明,必胜态一定可以只走一步,使得局面变为必败态。

必胜态时:

gif.latex?a_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20a_%7B3%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bn%7D%3Dx%20%5Cneq%200

设x二进制表示中最高一位为第k位。则a1~an中必然有ai,其第k位是1。由于gif.latex?a_%7Bi%7D%20%5C%20xor%20%5C%20x%3Ca_%7Bi%7D,于是我们在第i堆拿走gif.latex?a_%7Bi%7D-a_%7Bi%7D%5C%20xor%5C%20x个石子,使得拿走后,原式变为:

                                gif.latex?a_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20a_%7B3%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20%5Ba_%7Bi%7D-%28a_%7Bi%7D-a_%7Bi%7D%5C%20xor%5C%20x%29%5D..%5C%20xor%5C%20a_%7Bn%7D

                                gif.latex?%3Da_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20a_%7B3%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20%28a_%7Bi%7D%5C%20xor%5C%20x%29..%5C%20xor%5C%20a_%7Bn%7D

                                gif.latex?%3Da_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20a_%7B3%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bn%7D%5C%20xor%5C%20x

        ​​​​​​​        ​​​​​​​        ​​​​​​​        gif.latex?%3Dx%5C%20xor%20%5C%20x

        ​​​​​​​        ​​​​​​​        ​​​​​​​        gif.latex?%3D0

得证。

再证明,必败态无论怎么走,都会变成必胜态。 

必败态时:

gif.latex?a_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20a_%7B3%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bn%7D%3D0

设在第i堆拿走一些石子,使得gif.latex?a_%7Bi%7D%3Da_%7Bi%7D%5E%7B%27%7D,运用反证法,若走完该步之后,仍为必败态,即:

gif.latex?a_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bi%7D...%5C%20xor%5C%20a_%7Bn%7D%3D0

gif.latex?a_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bi%7D%5E%7B%27%7D...%5C%20xor%5C%20a_%7Bn%7D%3D0

两式子异或:

gif.latex?%5Ba_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bi%7D%5E%7B%27%7D...%5C%20xor%5C%20a_%7Bn%7D%5D%5C%20xor%5C%20%5Ba_%7B1%7D%20%5C%20xor%5C%20a_%7B2%7D%20%5C%20xor%5C%20...%5C%20xor%5C%20a_%7Bi%7D...%5C%20xor%5C%20a_%7Bn%7D%5D%3D0

gif.latex?a_%7B1%7D%5C%20xor%20%5C%20a_%7B1%7D%5C%20xor%5C%20a_%7B2%7D%5C%20xor%20%5C%20a_%7B2%7D...%5C%20xor%5C%20a_%7Bn%7D%5C%20xor%20%5C%20a_%7Bn%7D%5C%20xor%5C%20a_%7Bi%7D%5C%20xor%20%5C%20a_%7Bi%7D%5E%7B%27%7D%3D0

gif.latex?0%5C%20xor%5C%20a_%7Bi%7D%5C%20xor%20%5C%20a_%7Bi%7D%5E%7B%27%7D%3D0

gif.latex?a_%7Bi%7D%5C%20xor%20%5C%20a_%7Bi%7D%5E%7B%27%7D%3D0

gif.latex?a_%7Bi%7D%3D%20a_%7Bi%7D%5E%7B%27%7D

矛盾!因此必败态无论怎么走,必定变成必胜态。

        本次游戏中,由于每次必须拿走一些石子,所以游戏最终会结束,归于必败态(终局)。所以只需要一开始时,石子堆是必胜态,先手就必胜,后手永远处于必败态,直到游戏结束。若一开始石子是必败态,则先手必败。

#include<iostream>
using namespace std;

int main()
{
    int n;
    cin>>n;
    int res=0;
    while(n--)
    {
        int x;
        cin>>x;
        res^=x;
    }
    if(res) puts("Yes");
    else puts("No");
    return 0;
    
}

习题:台阶Nim游戏(考虑构成胜败态的因素)

 思考方式同经典Nim游戏,不过本题只需要考虑奇数阶上的台阶的石子数量异或和。

原因:1.如果后手移动奇数台阶,其情形等同经典Nim游戏,我们可以知道先手必然可以通过某种方式把异或和变成0.

           2.如果后手移动偶数台阶,先手只需要把后手移动的再移下去,局面等同于没有变化。

为什么是奇数台阶而不是偶数台阶:偶数台阶异或为0不是必败态,因为移动一号台阶不改变异或值,不符合必败态定义。

#include<iostream>
/*①当后手移动偶数台阶上的石子时,先手只需将对手移动的石子继续移到下一个台阶
这样奇数台阶的石子相当于没变,于是留给后手的又是奇数台阶异或为0的状态

②当后手移动奇数台阶上的石子时,留给先手的奇数台阶异或非0
根据经典Nim游戏,先手总能找出一种方案使奇数台阶异或为0*/


/*为什么不是偶数台阶?  偶数台阶异或为0不是必败态,因为移动一号台阶不改变异或值,不符合必败态定义*/
using namespace std;

const int N =100010;
int a[N];
int n;

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    int res=0;
    int i=1;
    while(i<=n)
    {
        res^=a[i];
        i+=2;
    }
    
    if(res) puts("Yes");
    else puts("No");
    return 0;
    
    
}

(2)集合—Nim游戏

074b4da041094b3e882f5d1be05dc2c9.png

 mex运算

        设一集合S,mex(S)即该集合所不能到达的最小自然数。

sg函数

        在有向图游戏中,对于每个节点x,设从x出发有k条边,分别到达后继节点y1,y2...yk。则我们定义SG(x)为以x的所有后继节点的函数值所构成的集合,执行一次mex运算的结果。

gif.latex?SG%28x%29%3Dmex%28%5Cleft%20%5C%7B%20SG%28y_%7B1%7D%29%2C%20SG%28y_%7B2%7D%29%2C...SG%28y_%7Bk%7D%29%2C%5Cright%20%5C%7D%29​​​​​​​

78f0ab503da5409abf2d4a0eac7ac928.png

 必胜/败状态

同上,必胜态(Gi为起点,默认出度为0的点为结束点,SG为0):

gif.latex?SG%28G_%7B1%7D%29%20%5C%20xor%20%5C%20SG%28G_%7B2%7D%29%20%5C%20xor%20%5C%20...SG%28G_%7Bn%7D%29%5Cneq%200

必败态:

gif.latex?SG%28G_%7B1%7D%29%20%5C%20xor%20%5C%20SG%28G_%7B2%7D%29%20%5C%20xor%20%5C%20...SG%28G_%7Bn%7D%29%3D0

首先,我们仅以一个有向图游戏为例:

则我们需要证明:1.若当前节点SG为0,则为必败态。2.若当前节点SG为1,则为必胜态。

 进一步来说,需要证明:1.SG为0的点只能走到SG不为0的节点。2.SG不为0的节点总能走到SG为0的节点。3.游戏总会结束,且终点的SG为0。

事实上,由于mex运算的特点,上述三点是显然成立的。

对于多个有向图游戏,证明方法同上:终局时,所有SG异或和为0;若不为0,总能使得其变为0;若为0,无论怎么走都走不回为0的状态。因此得证。

习题:拆分—Nim游戏

 分析:1.游戏必然会结束,结束时所有堆石子变成0。(题意解释:取走石子数为x的一堆石子,在增加两堆石子数都小于x的石子堆。这样单个石子堆的数量只能越来越小,最终变为0)。

2.一个局面的sg函数,是其拆1分局面sg函数的异或和(等同于多个有向图游戏)。

3.其余同集合Nim游戏,sg异或和为0则为必败态,反之为必胜态。

//这里填你的代码^^
#include<iostream>
#include<algorithm>
#include<unordered_set>
#include<cstring>
using namespace std;
const int N =110;
int f[N];
int sg(int x)
{
    if(f[x]!=-1) return f[x];
    unordered_set<int> S;
    for(int i=0;i<x;i++)
        for(int j=0;j<=i;j++)
            S.insert(sg(i)^sg(j));//合并子堆 的 游戏的局面

    for(int i=0;;i++)
        if(!S.count(i))
            return f[x]=i;

}


int main()
{
    memset(f,-1,sizeof f);
    int res=0;
    int n;
    cin>>n;
    while(n--) 
    {
        int x;
        cin>>x;
        res^=sg(x);
    }
    if(res) puts("Yes");
    else puts("No");
    return 0;
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3798278/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值