FZYZ-2071 A Simple Math Problem IX

时间:2024-04-07 20:05:25

P2071 -- A Simple Math Problem IX

时间限制:1000MS      内存限制:262144KB

状态:Accepted      标签:    数学问题-博弈论   无   无

Description

给定a,b,n,保证a≥2,b≥1,a^b≤n。两个人在玩游戏,每个人每次可以把a加1,或者把b加1,但是不能违反a^b<=n,无法再进行操作的人就输掉了这一场游戏。

假设两个人都足够聪明,按照最优策略进行游戏,问先手是否有必胜策略。

Input Format

第一行两个正整数 n 和 m。

接下来 m 行,每行两个正整数 a 和 b。保证 a≥2,b≥1,a^b≤n。

Output Format

m 行,如果对于这一对数字 a 和 b,如果先手有必胜策略,输出 “Yes”。否则输出 “No” (不含引号)

Sample Input

5 3
2 1
2 2
3 1

Sample Output

Yes
No
No

Hint

对于10%的数据,n=2

对于70%的数据,n<=1000

对于100%的数据,n<=10^9,m<=10^5

题解

不难想到,状态(a,b)可以转移到状态(a + 1, b)或者(a, b + 1)。设F[a][b]为该局面下先手胜败,1表示必胜,0表示必败,那么F[a][b] = (F[a + 1][b] & F[a][b +1]) ^ 1。但是数据范围太大,该怎么办呢?

首先,a和b必须满足a^b <= n,即log (a, n) >= b,又因为a >= 2,所以log (2, n) >= log(a, n),所以b <= log (2, n) <= 30。

其次,a^b <= n还可以推出当b >= 2时,a <= sqrt(n),这也是一个不大的数字。当b=1时,即便n很大,但随着a的递增,可供选择的b会逐渐减少,最后转移会变成一条链,所以根据奇偶性判断就好。

代码

 #include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#define MAXN 50010
#define MAXM 50
using namespace std; int N, M;
int j;
long long X;
int A, B;
int Lim;
bool F[MAXN][MAXM]; char ch; int aa, bb;
int Scan() {
while(ch=getchar(),(ch<''||ch>'')&&ch!='-');
ch=='-' ? (bb=,aa=) : (aa=ch-,bb=);
while(ch=getchar(),ch>=''&&ch<='')aa=aa*+ch-;
if(bb)aa=-aa;
return aa;
} int main() {
memset(F, , sizeof(F)); //F必须开bool
N = Scan();
Lim = int (sqrt(N));
if ((Lim + N) & ) F[Lim + ][] = ;
for (int i = Lim; i >= ; --i) {
for (j = , X = ; X <= N; X *= i, ++j); //X可能会爆int
for (--j; j; --j) F[i][j] = (F[i + ][j] & F[i][j + ]) ^ ;
}
for (M = Scan(); M; --M) {
A = Scan(); B = Scan();
if (B == && A > Lim) {
if ((N + A) & ) printf("Yes\n");
else printf("No\n");
}
else {
if (F[A][B]) printf("Yes\n");
else printf("No\n");
}
}
}