版权声明:原来这里可以拿来卖萌ヽ(・∀・)ノ https://blog.csdn.net/u012345506/article/details/85391482
设
表示
能够组成的最大的数;
设
表示第
个数的值。
那么有:
我们将所有函数都化为
:
我们再计算一下数列长度:
即为了表示
的所有数,产生的序列长度是
的。
容易用归纳证明任何
的情况都是
点:
显然成立;
其余情况由
可知先手不能一次取完
。同时后手必然取得
的最后一个石子,这使得先手再次进入
的局面,注意此时先手会受到上一次取的数量的限制,但由于其对于
性存在单调性同时最好情况为
点,所以先手依然处于
点。唯一的例外是
的情况,容易分析此时
性并没有变化。所以
的情况得证。
有了以上结论,容易证明
的情况都是
点:
只需要将
分解,设
,
按升序排列。
先手先一次取完
,因为分解中任意一对数的比都大于
,所以后手不得不面对
的
点,由之前的证明,可知后手必然拿到
的最后一个石子,这使得先手继续进入
的
点,以此类推。需注意此时先手第一次能够拿的最小值就是
,否则将会使自己变成
的
点的先手而输掉游戏。
所以我们可以在 下完成一次计算。
#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;
};