CF1537D题解

原题

CF1537D Deleting Divisors


思路概述

题意分析

一个博弈论游戏。给定一个整数 \(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\) 则必胜,反之必败)如下:

\[f(x)=\begin{cases}1,2|i\text{ or }i=2^k(2|k)\\0,\text{otherwise} \end{cases} \]


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;
}
posted @ 2022-04-20 17:46  UOB  阅读(125)  评论(0)    收藏  举报