超好资源
话说SG普及这么多年了SN还没普及,我寻思也没有什么很大的不同,也就扩个域,最后不还是用double算。。。
关于超现实数的小于等于符号在棋盘游戏上的意义,我是这样认为的:
在matrix67的博客中的这一段十分重要:
对于一个棋局 A ,如果我们完全交换左玩家和右玩家的地位,得到的新棋局就叫做棋局 A 的“反棋局”,记作 -A 。我们规定,对于棋局 A 和棋局 B 来说,如果棋局 A 和棋局 B 的反棋局同时进行(即游戏在 A + (-B) 上进行),左玩家必胜(不管谁先走),那么对于左玩家来说,棋局 A 比棋局 B 更好,或者说棋局 A 优于棋局 B ;反之,如果右玩家必胜(不管谁先走),那么对于左玩家来说,棋局 A 比棋局 B 更差,或者说棋局 A 劣于棋局 B 。如果 A + (-B) 是一个谁后走谁必胜的中立局面,我们就认为,棋局 A 和棋局 B 对于左玩家来说优劣相同,或者说棋局 A 平于棋局 B 。注意,今后不做特殊说明时,棋盘的优劣都是针对左玩家所说的。
棋局 A 劣于棋局 B 的另一个等价的定义就是,如果棋局 B 优于棋局 A ,我们就说棋局 A 劣于棋局 B 。如果棋局 B 优于棋局 A ,说明 B + (-A) 是左玩家必胜的,因而整个复合棋局的反棋局将会是右玩家必胜的。整个复合棋局的反棋局是什么?就是 B 的反棋局,加上 A 的反棋局的反棋局,也就是 A + (-B) 了。而 A + (-B) 对于右玩家必胜,这正好符合之前给出的 A 劣于 B 的定义。
最后,我们给出“劣于或平于”的形式化描述。如果棋局 A 劣于或者平于棋局 B ,这意味着 A + (-B) 有两种可能:右玩家必胜,或者谁后走谁必胜。总之,左玩家先走是绝不可能有必胜策略的。反过来,如果左玩家先走真的没有必胜策略(从而右玩家后走将必胜),这正好对应着那两种情况:如果此时再已知右玩家先走也没有必胜策略,那就是谁后走谁必胜;如果此时右玩家先走将必胜,那就属于右玩家必胜的情况。因此, A + (-B) 是右玩家必胜或者谁后走谁必胜的,当且仅当左玩家先走没有必胜策略。这等于是说,左玩家不会在 A + (-B) 上走出一种谁后走谁必胜或者左玩家必胜的局面。如果左玩家在 A 上面走一步,结果一定不会优于或平于 B ;同时, A 也一定不会优于或平于左玩家在 -B 上任意走一步(相当于右玩家在 B 上走一步)的结果。换句话说 A 的左集合中的元素不会优于或平于 B,同时 B 的右集合中的元素不会劣于或者平于 A 。至此为止,大家已经发现,棋局的加法运算,棋局的反棋局运算,以及棋局之间的“劣于或平于”关系,完全等同于超实数的公理和定义!为了保证所有的棋局都是超实数,我们强行规定,所有棋局也都必须满足超实数本身的构造限制,即左集合中没有元素优于或平于右集合中的某个元素。换句话说,对于左玩家来说,左玩家走了一步后所得的棋局,局势一定比右玩家走了一步后所得的棋局更险恶。更直观地说,我们这里考虑的都是先走比后走更不利、每多走一步就更接近死亡的游戏。
(原文有删减)
具体的意思是,我们判定两个游戏对左玩家的优劣,转化为判定在第二个游戏中左右玩家角色互换之后,两个游戏一起玩左玩家是否必胜。
为什么可以这么转化,因为可以用超现实数解决的不平等博弈问题,本质上都是在考虑左玩家能比右玩家多走几步,那么角色互换后仍能胜利,就说明第一个游戏多赚的步数比第二个多,这类游戏是一定会有停止的,只是多赚的步数并不总是整数,在复杂的不平等博弈问题不能简单的计算多赚的步数,于是使用超现实数计算。(至于更详细的原理我坑了。)
那么如果游戏
A
≤
B
A \leq B
A≤B,那么把
A
+
(
−
B
)
A+(-B)
A+(−B)一起玩(
−
B
-B
−B就是
B
B
B中角色互换),只能是右玩家必胜或后手必胜,那么如果左玩家先手,那么必败,所以
A
A
A的左集合中不可能有
x
∈
A
L
x\in AL
x∈AL满足
x
≥
B
x \geq B
x≥B,所以
A
A
A的左集合中所有的数都
<
B
\lt B
<B,同理
−
B
-B
−B的左集合中也都
<
A
\lt A
<A也就是
B
B
B的右集合中都
>
A
\gt A
>A。
然后发现这个定义它能把超现实数映射到实数上同时满足加法。。。
研究之美:
我就知道这是对的。。。
是的如果你认真仔细的看完了研究之美那基本一页有一半是公式和证明的小说(况且主人公还经常伪证,漏证),那么你肯定就知道这是对的。
发现能上中文维基了,贴几张图。
所以如果实数
A
,
B
A,B
A,B,我们把超现实数映射到他们上
A
=
{
A
L
∣
A
R
}
,
B
=
{
B
L
∣
B
R
}
A = \{A_L |A_R \} , B=\{B_L|B_R\}
A={AL∣AR},B={BL∣BR}
我们归纳证明一下,所以
A
L
A_L
AL等等也可以映射到实数,我们在下面也把他们看作实数。
首先我们只考虑
A
L
A_L
AL等
>
0
\gt 0
>0的情况。
那么
A
+
B
=
{
A
L
+
B
,
A
+
B
L
∣
A
+
B
R
,
B
+
A
R
}
A+B=\{A_L+B,A+B_L|A+B_R,B+A_R\}
A+B={AL+B,A+BL∣A+BR,B+AR}
根据定义可以知道
A
L
+
B
<
A
+
B
A_L+B < A+B
AL+B<A+B,其他同理。
如果
A
L
+
B
<
A
+
B
−
1
A_L+B < A+B-1
AL+B<A+B−1
那么
A
L
<
A
−
1
A_L<A-1
AL<A−1,这样
{
A
L
∣
A
R
}
=
A
−
1
\{A_L|A_R\} = A-1
{AL∣AR}=A−1矛盾。
所以
{
A
L
+
B
,
A
+
B
L
∣
A
+
B
R
,
B
+
A
R
}
=
A
+
B
\{A_L+B,A+B_L|A+B_R,B+A_R\} = A+B
{AL+B,A+BL∣A+BR,B+AR}=A+B
这个,最后一个式子虽然看上去和前面的式子一样,但是后面这个的意思是这个超现实数可以映射到
A
+
B
A+B
A+B,所以我们在计算超现实数的时候可以用实数代替,至于博弈转移,就求
A
L
A_L
AL及左玩家能转移的最大值,和
A
R
A_R
AR及右玩家能转移的最小值,然后再按这个规则转成实数即可(具体看开头博客)。
这个,博弈时可能会
L
≥
R
L\geq R
L≥R,所以就会有奇怪的事情发生,
那么
∗
*
∗相当于就是(大雾,其实是目前能做的就是
∗
=
S
G
*=SG
∗=SG的情况)
S
G
SG
SG函数那个领域的知识,当SurrealNumber=0的时候再考虑
S
G
SG
SG即可,因为
L
=
R
L=R
L=R所以两边操作相同直接算
S
G
SG
SG。
例题:
对于一个
w
×
h
w\times h
w×h的矩形。
A
l
i
c
e
Alice
Alice只可以横着切成等大小的多块矩形并且需要满足长宽皆为整数。
B
o
b
Bob
Bob只可以竖着切成等大小的。。。
问
A
l
i
c
e
Alice
Alice先手,能否必胜。
s n ( a , b ) = { max n ∣ a , n > 1 s n ( a n , b ) × n ∣ min n ∣ b , n > 1 s n ( a , b n ) × n } sn(a,b) = \{\max_{n|a,n>1}sn(\frac an,b)\times n|\min_{n|b,n>1} sn(a,\frac bn)\times n\} sn(a,b)={maxn∣a,n>1sn(na,b)×n∣minn∣b,n>1sn(a,nb)×n}
f o r m w o r k c o d e \mathcal formwork\ code formwork code
#include<bits/stdc++.h>
#define maxn 105
#define db double
#define LL long long
#define inf (LL)(1e10)
using namespace std;
bool usd[maxn][maxn];
struct frac{//a >> b
LL a,b;
frac(const LL &a=0,const LL &b=0):a(a),b(b){}
frac operator +(const frac &B)const{
LL t = max(b,B.b);
return frac((a<<(t-b))+(B.a<<(t-B.b)),t);
}
frac operator -(const frac &B)const{
LL t = max(b,B.b);
return frac((a<<(t-b))-(B.a<<(t-B.b)),t);
}
frac operator *(int B)const{
frac r(*this);
for(;B%2==0 && r.b;r.b--,B>>=1);
r.a *= B;
return r;
}
bool operator <(const frac &B)const{
return ((*this) - B) . a < 0;
}
frac floor(LL k=0)const{
if(k >= b) return *this;
return frac(a>>(b-k),k);
}
frac ceil(LL k=0)const{
if(k >= b) return *this;
frac t = (*this).floor(k);
if(!(t < (*this)) && !((*this) < t)) return t;
return frac(t.a+1,k);
}
}sn[maxn][maxn],snw[100005];
frac get_sn(int w,int h){
if(usd[w][h]) return sn[w][h];usd[w][h] = 1;
frac l(-inf,0),r(inf,0);
for(int i=2;i<=w;i++) if(w%i==0) l = max(l , get_sn(w/i,h) * i);
for(int i=2;i<=h;i++) if(h%i==0) r = min(r , get_sn(w,h/i) * i);
if((l.a < 0 && r.a > 0) || (l.a == 0 && r.a == 0)) return sn[w][h]=frac(0,0);
frac t = (l+frac(1,0)).floor();
if(t < r){
if(!(l<frac(0,0))) return sn[w][h]=t;
return sn[w][h] = (r-frac(1,0)).ceil();
}
for(int k=1;;k++){
frac j=(l+frac(1,k)).floor(k);
if(j < r) return sn[w][h] = j;
}
}
int main(){
int n,a,b,w,h;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d%d%d",&a,&b,&w,&h),snw[i] = get_sn(w,h) + snw[i-1];
int Q;
scanf("%d",&Q);
for(;Q--;){
int l,r;
scanf("%d%d",&l,&r);
frac t = snw[r] - snw[l-1];
if(t.a > 0) puts("Yes");
else puts("No");
}
}
这个
s
u
r
r
e
a
l
n
u
m
b
e
r
surreal \ number
surreal number还有更快的算法。
容易发现答案和
w
,
h
w,h
w,h的质因数个数有关。
设
x
x
x的质因数个数为
d
(
x
)
d(x)
d(x)
可以发现,当
d
(
w
)
>
d
(
h
)
d(w) > d(h)
d(w)>d(h)的时候,右玩家怎么操作,左玩家都可以让
d
(
w
)
≥
d
(
h
)
d(w) \geq d(h)
d(w)≥d(h),并且至少有一个块
d
(
w
)
>
d
(
h
)
d(w) > d(h)
d(w)>d(h),那么
s
u
r
r
e
a
l
n
u
m
b
e
r
surreal \ number
surreal number就恒
>
0
>0
>0
同理
d
(
w
)
<
d
(
h
)
d(w) < d(h)
d(w)<d(h)时
s
n
(
w
,
h
)
<
0
sn(w,h) < 0
sn(w,h)<0
d
(
w
)
=
d
(
h
)
d(w) = d(h)
d(w)=d(h)时,
s
n
(
w
,
h
)
sn(w,h)
sn(w,h)的左集合都
<
0
<0
<0,右集合都
>
0
>0
>0,根据我们的定义可以得到
s
n
(
w
,
h
)
=
0
sn(w,h) = 0
sn(w,h)=0。
那么当
d
(
w
)
=
d
(
h
)
+
1
d(w) = d(h) + 1
d(w)=d(h)+1时,
s
n
(
w
,
h
)
=
1
sn(w,h) = 1
sn(w,h)=1,因为左集合的都
=
0
=0
=0
d
(
w
)
=
d
(
h
)
+
2
d(w) = d(h) + 2
d(w)=d(h)+2时,
s
n
(
w
,
h
)
=
w
sn(w,h) =w
sn(w,h)=w最大的质因子
+
1
+1
+1。(见上面公式即可理解。)
可以归纳一下,将每个数
x
x
x的质因子排个序放在
d
[
x
]
d[x]
d[x]之后就是:
LL r=0;
for(int i=d[b].size();i<d[a].size();i++) r=r*d[a][i]+1;
至此就可以 O ( log max ( a , b ) ) O(\log \max(a,b)) O(logmax(a,b))求出 s n ( a , b ) sn(a,b) sn(a,b)的值。