设
g
(
i
)
g(i)
g(i)表示
[
1
,
i
]
[1,i]
[1,i]能够组成的最大的数;
设
f
(
i
)
f(i)
f(i)表示第
i
i
i个数的值。
那么有:
f
(
i
)
=
g
(
i
−
1
)
+
1
,
g
(
i
)
=
f
(
i
+
1
)
−
1
g
(
i
)
=
f
(
i
)
+
g
(
a
)
∣
a
=
M
A
X
{
k
f
(
a
)
<
f
(
i
)
}
f(i)=g(i-1)+1,g(i)=f(i+1)-1\\ g(i)=f(i)+g(a)|a=MAX\{kf(a)<f(i)\}
f(i)=g(i−1)+1,g(i)=f(i+1)−1g(i)=f(i)+g(a)∣a=MAX{kf(a)<f(i)}
我们将所有函数都化为
f
f
f:
f
(
i
)
=
f
(
i
−
1
)
+
g
(
a
)
+
1
∣
a
=
M
A
X
{
k
f
(
a
)
<
f
(
i
−
1
)
}
=
f
(
i
−
1
)
+
f
(
a
+
1
)
∣
a
=
M
A
X
{
k
f
(
a
)
<
f
(
i
−
1
)
}
=
f
(
i
−
1
)
+
f
(
a
)
∣
a
=
M
I
N
{
k
f
(
a
)
≥
f
(
i
−
1
)
}
f(i)=f(i-1)+g(a)+1|a=MAX\{kf(a)<f(i-1)\}\\ =f(i-1)+f(a+1)|a=MAX\{kf(a)<f(i-1)\}\\ =f(i-1)+f(a)|a=MIN\{kf(a)\ge f(i-1)\}
f(i)=f(i−1)+g(a)+1∣a=MAX{kf(a)<f(i−1)}=f(i−1)+f(a+1)∣a=MAX{kf(a)<f(i−1)}=f(i−1)+f(a)∣a=MIN{kf(a)≥f(i−1)}
我们再计算一下数列长度:
f
(
i
)
f
(
i
−
1
)
=
1
+
f
(
a
)
f
(
i
−
1
)
≥
1
+
1
k
\frac {f(i)} {f(i-1)}=1+\frac {f(a)} {f(i-1)}\\ \ge1+\frac 1 k
f(i−1)f(i)=1+f(i−1)f(a)≥1+k1
即为了表示
[
1
,
n
]
[1,n]
[1,n]的所有数,产生的序列长度是
O
(
l
o
g
1
+
1
k
n
)
O(log_{1+\frac 1 k}n)
O(log1+k1n)的。
容易用归纳证明任何
n
=
f
(
i
)
n=f(i)
n=f(i)的情况都是
P
P
P点:
n
=
2
,
i
=
2
n=2,i=2
n=2,i=2显然成立;
其余情况由
f
(
i
)
=
f
(
i
−
1
)
+
f
(
a
)
,
k
f
(
a
)
≥
f
(
i
−
1
)
f(i)=f(i-1)+f(a),kf(a)\ge f(i-1)
f(i)=f(i−1)+f(a),kf(a)≥f(i−1)可知先手不能一次取完
f
(
a
)
f(a)
f(a)。同时后手必然取得
f
(
a
)
f(a)
f(a)的最后一个石子,这使得先手再次进入
n
=
f
(
i
−
1
)
n=f(i-1)
n=f(i−1)的局面,注意此时先手会受到上一次取的数量的限制,但由于其对于
N
P
NP
NP性存在单调性同时最好情况为
P
P
P点,所以先手依然处于
P
P
P点。唯一的例外是
f
(
a
)
=
1
f(a)=1
f(a)=1的情况,容易分析此时
N
P
NP
NP性并没有变化。所以
n
=
f
(
i
)
∣
i
>
2
n=f(i)|i>2
n=f(i)∣i>2的情况得证。
有了以上结论,容易证明
n
≠
f
(
i
)
n\neq f(i)
n̸=f(i)的情况都是
N
N
N点:
只需要将
n
n
n分解,设
n
=
∑
i
=
1
a
i
n=\sum_{i=1} a_i
n=∑i=1ai,
a
a
a按升序排列。
先手先一次取完
a
1
a_1
a1,因为分解中任意一对数的比都大于
k
k
k,所以后手不得不面对
n
=
a
2
n=a_2
n=a2的
P
P
P点,由之前的证明,可知后手必然拿到
a
2
a_2
a2的最后一个石子,这使得先手继续进入
n
=
a
3
n=a_3
n=a3的
P
P
P点,以此类推。需注意此时先手第一次能够拿的最小值就是
a
1
a_1
a1,否则将会使自己变成
n
=
a
1
n=a_1
n=a1的
P
P
P点的先手而输掉游戏。
所以我们可以在 O ( l o g 1 + 1 k n ) O(log_{1+\frac 1 k}n) O(log1+k1n)下完成一次计算。
#include<iostream>
using namespace std;
typedef long long ll;
int ar[2000010],n,k,ct=0;
void cl(){
int i,a;for(scanf("%d %d",&n,&k),ar[1]=1,i=2,a=1;ar[i-1]+1<n;++i){
for(;(ll)ar[a]*k<ar[i-1];++a);
ar[i]=ar[a]+ar[i-1];
}
printf("Case %d: ",++ct);
if(ar[i-1]==n)printf("lose\n");
else{
for(--i;;--i){
if(n>=ar[i])n-=ar[i];
if(!n){printf("%d\n",ar[i]);break;}
}
}
};
int main(){
int t;scanf("%d",&t);
while(t--)cl();
return 0;
};