第一次考场写博弈论,好激动
暴力求
s
g
sg
sg 函数还是很好做的
s
g
(
x
)
=
m
e
x
(
s
g
(
i
)
)
(
x
−
x
b
i
≤
i
<
x
)
sg(x)=mex(sg(i))(x-\frac{x}{b_i}\le i< x)
sg(x)=mex(sg(i))(x−bix≤i<x)
然后把表打出来过后发现
s
g
(
x
)
=
s
g
(
x
−
x
b
i
−
1
)
(
x
%
b
i
!
=
0
)
sg(x)=sg(x-\frac{x}{b_i}-1)(x\%b_i!=0)
sg(x)=sg(x−bix−1)(x%bi!=0)
s
g
(
x
)
=
x
b
i
(
b
i
∣
x
)
sg(x)=\frac{x}{b_i}(b_i|x)
sg(x)=bix(bi∣x)
证明的话考虑一下求
m
e
x
mex
mex 的过程就可以了
这样就可以拿
70
p
t
s
70pts
70pts 了
发现
b
i
b_i
bi 大的时候跳的次数多但是
x
b
i
\frac{x}{b_i}
bix 的取值不多
b
i
b_i
bi 小的时候跳得飞快
于是当
b
i
≤
a
i
b_i\le \sqrt {a_i}
bi≤ai 时,暴力跳,最多
a
i
\sqrt {a_i}
ai 次
当
b
i
>
a
i
b_i> \sqrt {a_i}
bi>ai 时,找到
x
b
i
\frac{x}{b_i}
bix 相同的一段一起减掉即可
考场上慢慢想感觉对
s
g
sg
sg 函数有了一些新的理解
首先一个常识是
s
g
(
x
)
sg(x)
sg(x) 为所有子状态的
m
e
x
mex
mex,还有就是
s
g
sg
sg 为 0 那么必败
为什么呢?
因为一个
s
g
sg
sg 为 0 的话那么它的所有子状态都不为 0
一个
s
g
sg
sg 不为 0 只需要一个子状态为 0
这就对应了一个状态必败当且仅当它的所有子状态为必胜状态
一个必胜状态只要可以到一个必败状态它就必胜
而一个游戏的
s
g
sg
sg 为所有子游戏的
s
g
sg
sg 的异或和又是为什么呢?
又有一个常识是
s
g
sg
sg 的异或和为 0 的话那么必败
考虑到如果异或和为 0 那么任意走一步都会使异或和不为 0
如果异或和不为 0 ,假设异或和是
x
x
x,最大位的 1 在第 k 位,
那么一定可以找到一个
a
i
a_i
ai 使得
x
x
x 的
a
i
a_i
ai 的第
k
k
k 位为 1
然后把
a
i
a_i
ai 变成
a
i
a_i
ai 异或
x
x
x ,就可以到达一个异或和为 0 的局面
因为取的是最大的一位,那么
a
i
a_i
ai 异或了
x
x
x 过后一定小于
a
i
a_i
ai
而根据
s
g
sg
sg 的取
m
e
x
mex
mex 一个状态一定能到达所有小于它的状态
之前学没有懂,打了个表玩了玩竟然就有感觉了!
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int T, n;
int sg(int a, int b){
if(a % b == 0) return a / b;
return sg(a - a / b - 1, b);
}
int SG(int a, int b){
int sz = sqrt(a);
if(a / b > sz) return sg(a, b);
while(a % b){
int t = a / b + 1, nxt = a / b * b;
a -= (a - nxt) / t * t;
if(a % b) a -= a / b + 1;
} return a / b;
}
int main(){
T = read();
while(T--){
n = read();
int ans = 0;
for(int i = 1; i <= n; i++){
int a = read(), b = read();
ans ^= SG(a, b);
} if(ans) puts("Setsuna");
else puts("Kazusa");
} return 0;
}