LINK

A l i c e Alice Alice B o b Bob Bob做游戏, A l i c e Alice Alice先手.

每次每个人可以从其中一堆拿走 k ( k > 0 ) k(k>0) k(k>0)个石子,同时从另一堆拿走 s ∗ k ( s > = 0 ) s*k(s>=0) sk(s>=0)个石子

谁不能操作谁就输了,问胜负情况.

2021牛客暑期多校训练营1 Alice and Bob(结论,sg打表|优化)_复杂度

容易想到定义 f [ i ] [ j ] f[i][j] f[i][j]表示两堆式子分别剩下 i , j i,j i,j个的胜负情况

可以暴力枚举转移,复杂度为 O ( n 3 l o g ( n ) ) O(n^3log(n)) O(n3log(n))

然后发现 n n n不大可以暴力打表,比赛中也有人这么干了

不过 n n n大一点就没办法了

结论

当一堆石子个数为 i i i时,另一种石子只有一种情况能使后手必胜

证明

反证法,假设 ( i , p ) (i,p) (i,p) ( i , q ) (i,q) (i,q)都是后手必胜(其中 p > q p>q p>q)

设现在局面为 ( i , p ) , (i,p), (i,p),那么先手第一次从第二堆式子拿走 p − q p-q pq

局面变为 ( i , q ) (i,q) (i,q),这不就说明先手其实是必胜的吗?

所以状态 ( i , p ) (i,p) (i,p)是必胜态

于是,可以发现 f [ i ] [ j ] f[i][j] f[i][j]中后手必胜的情况最多只有 n n n

于是可以把后手必胜的状态提取出来,能转移到这个状态的 f [ i ] [ j ] f[i][j] f[i][j]都是先手必胜

复杂度 O ( n 2 l o g ( n ) ) O(n^2log(n)) O(n2log(n))

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+10;
bool f[maxn][maxn];
vector<int>ans;
void init()
{
	int z = 0, w = 0;
	for(int i=0;i<=5000;i++)
	for(int j=0;j<=5000;j++)
	{
		if( f[i][j] )	continue;//先手必胜跳过
		for(int fi=1;i+fi<=5000;fi++)//从第一堆里面拿走fi个 
		for(int se=0;j+se<=5000;se+=fi)//从第二堆里面拿走se个 
			f[i+fi][j+se] = true;
		for(int fi=1;j+fi<=5000;fi++)
		for(int se=0;i+se<=5000;se+=fi)
			f[i+se][j+fi] = true;		 
	}
}
int main()
{
	init();
	int t; cin >> t;
	while( t-- )
	{
		int n,m; cin >> n >> m;
		if( f[n][m] )	cout << "Alice\n";
		else	cout << "Bob\n";
	}
}