CF1537D题解
原题
思路概述
题意分析
一个博弈论游戏。给定一个整数 \(n(n∈[1,10^9])\) ,Alice和Bob二人轮流从中减去当前 \(n\) 的因子(Alice先手),最终无法操作的人失败。求两人中谁有必胜策略。
思路分析
最初笔者将题中“减去”操作误读为“除去”,误以为可以用分解质因数和巴什博奕。但本题是在当前 \(n\) 中减去其因子,故不能按上述方法解决。
由于本题数据规模 \(n∈[1,10^9]\) ,按一般博弈论题目DP打表的解法显然不可行,所以笔者推测本题是一道结论题。
关于思考方向,因为题目数据规模较大,因此算法思路应该从 \(n\) 本身的性质出发。综上,笔者选择从奇偶性开始考虑问题性质。
算法实现
从最简单的必败态出发
由于题目中对必败态的定义“某一方无法进行操作则判输”,又因为每次减去的因子 \(i\) 不能为 \(1\) 或 \(n\) ,所以可以大胆推测的是,当出现 \(n=1\) 时,先手方无法操作,所以必然失败。
根据博弈论中胜负态的转移原则(必败态的所有前驱态必然都为必胜态,必胜态至少有一个后继态为必败态),我们可以推出一个如下的转移方程雏形。
状态转移推导
如上文所述,笔者认为本题思路方向应该放在 \(n\) 的奇偶性考虑,又根据上文中状态转移的原则我们可以得到状态转移的基本思路。
首先,对于先手方面对 \(\frac{n}{2}∈N^*\) ( \(n\) 为偶数)的情况,必然存在因子 \(p|n\) 有 \(p\) 为奇数和偶数两种情况。根据小学数学基础我们可以知道的是,一个偶数减去一个偶数仍然为偶数,即状态不发生改变;一个偶数减去一个奇数结果为奇数,状态改变。在第二种情况中,若对方再将奇数转化为偶数,就又回到了当前状态。显然地,这种状态互相转化的过程必然以对方拿到 \(n=1\) 的情况为结束,因此当 \(n\) 为偶数时当前操作方处于必胜态。
特别地,当 \(n=2^k(k∈N^*)\) 时,先手方始终可以取走 \(n\) 的一半避免使对方拿到必胜态。但这种转移必定以某一方取到 \(n=1\) 告终,而决定先手方是否失败的要素就是这种转移的次数 \(k\) ,即 \(k|2\) ,先手方必胜,反之必败。
对于 \(\frac{n}{2}∉N^*\) ( \(n\) 为奇数)的情况,其所有因子必然都是奇数,所以先手方的任何操作必然使 \(n'\) 为偶数,即将必胜态送给对方,所以处于必败态。
由上文就得到了本题的判断函数(设函数值为 \(1\) 则必胜,反之必败)如下:
AC code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<ctime>
#define RI register int
using namespace std;
int T,n,m;
inline int read();
inline void print(int x);
int main()
{
for(T=read();T;--T)
{
n=read();
if(n&1) puts("Bob");
else
{
RI clc=log2(n*1.0);
if(1<<clc==n && clc&1) puts("Bob");
else puts("Alice");
}
}
return 0;
}
inline int read(void)
{
char putin;bool isneg=false;RI ret=0;
putin=getchar();
while((putin>'9' || putin<'0') && putin!='-')
putin=getchar();
if(putin=='-')
{
isneg=true;
putin=getchar();
}
while(putin>='0' && putin<='9')
{
ret=(ret<<3)+(ret<<1)+(putin&15);
putin=getchar();
}
return isneg?-ret:ret;
}
inline void print(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x>9) print(x/10);
putchar(x%10+'0');
return;
}
浙公网安备 33010602011771号