首页 > 其他分享 >SG函数

SG函数

时间:2023-08-23 16:26:15浏览次数:33  
标签:局面 函数 int 必败 oplus SG mex

SG 函数

先定义,SG 函数对应有向无环图(DAG)上的一种游戏:有一枚棋子在起点上,每次可以沿着边往后移动,谁无法移动谁就输了。

公平组合游戏可以转换成他,只需要将局面中的所有状态看成一个节点,合法行动看成有向边。

判断必胜需要求解的就是起点的 SG

对于终点(没有出边),\(SG=0\)。

对于其他点 \(u\),设它的出去的点为 \(v\),则 \(SG(u)=mex(SG(v_1),SG(v_2),\dots)\)。

\(mex\) 表示没有出现的最小自然数,如 \(mex(2,3)=0,mex(1,0)=2,mex(0)=1\)。

然后对于起点,若 \(SG=0\) 必败,反之必胜。

证明:

  1. 全 \(0\) 局面 \(SG=0\) 先手必败。
  2. 对于当前局面若 \(SG\neq0\),那么说明可以到达 \(0\),走到 \(SG=0\) 的点上,那么抛给对手就是 \(SG=0\)。
  3. 若 \(SG=0\),说明无法到达 \(0\),那么只能给对手 \(SG\neq 0\)。

若先手局面为 \(SG\neq 0\),那么他可以给对手为0,然后对手只能给他非0,最后对手肯定得到全0,必败,先手必胜。

反之,若为0,那么他只能给对手非0,对手给他0,最后他肯定得到全0,必败,后手必胜。

那有的同学肯定好奇:SG为什么不直接取0/1?

那是因为可能会出现多张有向图的情况,此时的结论同 nim:将各SG异或,如果为0,先手必败,否则必胜。

设总值为 \(x\)。

证明分三步,类似于 nim

  1. 全 \(0\) 局面 \(x=0\) 先手必败。

  2. 对于当前局面若 \(x\neq0\),存在某种方式将其变为 \(0\):

    考虑 \(x\) 的最高的为 \(1\) 二进制位(记为 \(k\)),原序列中肯定有至少一个(奇数个)图的SG第 \(k\) 位为 \(1\)。(设为 \(a_i\))所以 \(a_i\oplus x<a_i\)(前面的位保持不变,然后第 \(k\) 位变为 \(0\),后面就算均增加也会减少)。那么由于 \(mex=a_i\) 时可以走到 \([1,a_i)\),所以,让这个有向图走到 \(SG=a_i\oplus x\) 的点上。那么新的异或值就是 \(\oplus_{i=1}^n a_i\oplus x=x\oplus x=0\),抛给对手一个 \(x=0\) 的局面。

  3. 对于当前局面若 \(x=0\),不存在任何方式将其变为 \(0\):

    用反证法,假设存在某种方式,且走动的记为 \(a_i\),那么有 \(\oplus_{j=1}^n a_i=0\),且 \(\oplus_{j=1}^{i-1} a_j\oplus a_i'\oplus_{j=i+1}^{n} a_j=0\),两个式子异或,其余的数都消去,剩下 \(a_i\oplus a_i'=0\),即 \(a_i=a_i'\),而 mex 定义为没有出现过的最小者,只能走到 \([1,a_i)\),矛盾,所以不存在。

而当先手的局面 \(x>0\) 时,可以构造 \(x=0\),后手又给出 \(x>0\) 的局面,先手再给予 \(x=0\) 的局面 \(\dots\),后手总是持有 \(x=0\) 的局面,而最终必败状态全 \(0\) 就是 \(x=0\),所以后手必定会取到,即后手必败。

反之 \(x=0\),先手不管怎么搞 \(x>0\),后手再将其变为 \(0\),所以先手总是持有 \(x=0\) 的局面,而最终必败状态全 \(0\) 就是 \(x=0\),所以先手必定会取到,即先手必败。

所以,我们只需要计算异或和,判断是否等于零即可。

对于本题,只需要分堆考虑,对于每一堆,只需要将石子个数当作状态,然后考虑集合中可以取的(比如 \(1,2,3\),那么就向 \(x-1,x-2,x-3\) 连边),求一遍 \(SG\) 即可。

#include<cstdio>
#include<vector>
using namespace std;
const int N=110;
bool st[N];
int k,s[N],n,x,res,f[N*N];
int sg(int x){
    if(f[x])return f[x];
    vector<int>g;
    for(int i=1;i<=k;++i)
        if(x>=s[i])g.push_back(sg(x-s[i]));
    for(int v:g)st[v]=1;
    for(int i=0;;++i)
        if(!st[i]){
            for(int v:g)st[v]=0;
            return f[x]=i;
        }
}
int main(){
    scanf("%d",&k);
    for(int i=1;i<=k;++i)scanf("%d",s+i);
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        int x;
        scanf("%d",&x);
        res^=sg(x);
    }
    if(res)puts("Yes");
    else puts("No");
    return 0;
}

标签:局面,函数,int,必败,oplus,SG,mex
From: https://www.cnblogs.com/wscqwq/p/17651966.html

相关文章

  • 无涯教程-PHP Interview Questions函数
    亲爱的读者,这些PHP编程语言面试问题是专门设计的,目的是让您熟悉在采访中可能会遇到的关于PHP编程语言主题的问题的性质。根据我的经验,优秀的面试官几乎不会计划在面试过程中提出任何特定的问题,通常,问题是从该主题的一些基本概念开始的,然后根据进一步的讨论和您的回答继续......
  • 两种修改字节码改变函数执行方式
    1示例publicReturnTypefunction(){try{Object[]args=newObject[]{};RetbeforeRet=callOnBefore(args...);//返回对象if(beforeRet.state==1){return(ReturnType)beforeRet.respo......
  • 无涯教程-PHP - 错误处理函数
    这些是处理错误处理和日志记录的功能。它们使您可以定义自己的错误处理规则,以及修改错误记录方式。运行时配置这些功能的行为受php.ini中的设置影响,这些设置在下面定义。NameDefaultChangeableChangelogerror_reportingNULLPHP_INI_ALLdisplay_errors"1"PHP_INI_ALL......
  • 无涯教程-PHP - preg_split()函数
    preg_split()-语法arraypreg_split(stringpattern,stringstring[,intlimit[,intflags]]);preg_split()函数的操作与split()完全相同,只不过正则表达式被接受为pattern的输入参数。如果指定了可选的输入参数limit,则仅返回子字符串的限制数量。标志可以是以下标志......
  • 5、oracle迁移到postgres-oracle中使用的`nvl`函数更改为统一的`coalesce`函数
    目录oracle迁移到postgres-oracle中使用的nvl函数更改为统一的coalesce函数1、oracle的nvl函数2、postgre的coalesce函数oracle迁移到postgres-oracle中使用的nvl函数更改为统一的coalesce函数nvl函数与coalesce函数都是值非空时,给默认值,oracle中也存在coalesce函数1、oracle的......
  • 4、oracle迁移到postgres-oracle中使用的`decode`函数使用`case when`统一语法
    目录oracle迁移到postgres-oracle中使用的decode函数使用casewhen统一语法1、oracle的decode语法2、postgres的casewhenoracle迁移到postgres-oracle中使用的decode函数使用casewhen统一语法oracle中也有使用casewhen语法,使用decode函数比较简洁。1、oracle的decode语法匹......
  • Python基础入门学习笔记 021函数:lambda表达式
    lambda表达式的作用•Python写一些执行脚本时,使用lambda就可以省下定义函数过程,比如说我们只是需要写个简单的脚本来管理服务器时间,我们就不需要专门定义一个函数然后再写调用,使用lambda就可以使得代码更加精简。•对于一些比较抽象并且整个程序执行下来只需要调用一两次的函......
  • Python基础入门学习笔记 022 函数:递归是神马
    汉诺塔游戏 树结构的定义 谢尔宾斯基三角形递归求阶乘•写一个求阶乘的函数–正整数阶乘指从1乘以2乘以3乘以4一直乘到所要求的数。–例如所给的数是5,则阶乘式是1×2×3×4×5,得到的积是120,所以120就是4的阶乘。•假设我们n的值传入是5,那么: 实例:求阶乘1deffac......
  • Python基础入门学习笔记 018 函数:灵活即强大
    形参和实参>>>defMyFirstFunction(name):'函数定义过程中的name是叫形参'#因为Ta只是一个形式,表示占据一个参数位置print('传递进来的'+name+'叫做实参,因为Ta是具体的参数值!')>>>MyFirstFunction('小甲鱼')传递进来的小甲鱼叫做实参,因为Ta是具体的参数值!关键字参数......
  • 第2章 函数的连续性
    第2章函数的连续性§2.1集合的映射(上.P55)定义2.1.1一个从A到B的映射;集合A叫作映射的定义域;f(x)叫作x在映射之下的像或映射在x上的值定义2.1.1设\(A\),\(B\)是两个集合,如果\(f\)是一种规律,使得对\(A\)中的每一个元素\(x\),\(B\)中有唯一确定的元素——记为\(f(x)\)——......