hdu 2580 a simple stone game

版权声明:原来这里可以拿来卖萌ヽ(・∀・)ノ https://blog.csdn.net/u012345506/article/details/85391482

g ( i ) g(i) 表示 [ 1 , i ] [1,i] 能够组成的最大的数;
f ( i ) f(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 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 ) = 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
即为了表示 [ 1 , n ] [1,n] 的所有数,产生的序列长度是 O ( l o g 1 + 1 k n ) O(log_{1+\frac 1 k}n) 的。

容易用归纳证明任何 n = f ( i ) n=f(i) 的情况都是 P P 点:
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 ( a ) f(a) 。同时后手必然取得 f ( a ) f(a) 的最后一个石子,这使得先手再次进入 n = f ( i 1 ) n=f(i-1) 的局面,注意此时先手会受到上一次取的数量的限制,但由于其对于 N P NP 性存在单调性同时最好情况为 P P 点,所以先手依然处于 P P 点。唯一的例外是 f ( a ) = 1 f(a)=1 的情况,容易分析此时 N P NP 性并没有变化。所以 n = f ( i ) i > 2 n=f(i)|i>2 的情况得证。

有了以上结论,容易证明 n f ( i ) n\neq f(i) 的情况都是 N N 点:
只需要将 n n 分解,设 n = i = 1 a i n=\sum_{i=1} a_i a a 按升序排列。
先手先一次取完 a 1 a_1 ,因为分解中任意一对数的比都大于 k k ,所以后手不得不面对 n = a 2 n=a_2 P P 点,由之前的证明,可知后手必然拿到 a 2 a_2 的最后一个石子,这使得先手继续进入 n = a 3 n=a_3 P P 点,以此类推。需注意此时先手第一次能够拿的最小值就是 a 1 a_1 ,否则将会使自己变成 n = a 1 n=a_1 P P 点的先手而输掉游戏。

所以我们可以在 O ( l o g 1 + 1 k n ) O(log_{1+\frac 1 k}n) 下完成一次计算。

#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;
};

猜你喜欢

转载自blog.csdn.net/u012345506/article/details/85391482