2021牛客多校1 A
题意:
两人博弈,两堆石子,每次操作从一堆中取 k 个,另一堆中取 k * s 个,谁先没得取谁输, N <= 5000。
思路:
结论:
当一堆石子数量为 i 时,要想后手取胜,第二堆石子仅有一种可能。
反证法:
假设(i,q)和(i,p)都是后手获胜,且 q > p,则当面临(i,q)局面时,先手可以取走 q - p 个石子使局面变为(i,p)且为后手,此时必胜,与前提矛盾。
然后开始暴力
数组 f[i][j] = 1 表示一堆石子为 i ,另一堆为 j 时,先手必胜。
可以看出,f[0][0] = 0。
因此从(0,0)开始,枚举两堆石子的个数,若 f[i][j] = 0 ,则 f[i + k][j + s * k] = f[i + s * k][j + k] = 1
显然,暴力时间复杂度为
O
(
n
4
)
O(n^4)
O(n4) 会超时,但是由结论可知,对于每个 i ,任意的 j 构成的局面,至多有一种先手必败态(即后手必胜),因此复杂度可减低至大约
O
(
n
3
)
O(n^3)
O(n3) ,加上奇奇怪怪的小优化,850ms跑过…
代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#include<math.h>
#include<queue>
#include<stack>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double dd;
typedef pair<int, int> pii;
typedef pair<dd, dd> pdd;
const int MAXN = 5001; //这里开成 5010 会T,非常神秘
const int inf = 1e9 + 7;
const ll llinf = 1e17 + 7;
const int MAXM = 5005000;
bool f[MAXN][MAXN];//开成 int 会T,非常神秘×2
int main()
{
for (int i = 0;i <= 5000;i++)
{
for (int j = 0;j <= 5000;j++)
{
if (!f[i][j])
{
for (int k = 1;k + i <= 5000;k++)
{
for (int s = 0;s * k + j <= 5000;s++)
{
f[i + k][j + s * k] = 1;
}
}
for (int k = 1;k + j <= 5000;k++)
{
for (int s = 0;s * k + i <= 5000;s++)
{
f[i + s * k][j + k] = 1;
}
}
}
}
}
int T;
scanf("%d", &T);
while (T--)
{
int n, m;
scanf("%d%d", &n, &m);
if (f[n][m] == 1) puts("Alice");
else puts("Bob");
}
}