ACM训练联盟周赛 C题 Alice和Bob的Nim游戏

题目描述

众所周知,Alice和Bob非常喜欢博弈,而且Alice永远是先手,Bob永远是后手。

Alice和Bob面前有3堆石子,Alice和Bob每次轮流拿某堆石子中的若干个石子(不可以是0个),拿到所有石子中最后一个石子的人获胜。这是一个只有3堆石子的Nim游戏。

Bob错误的认为,三堆石子的Nim游戏只需要少的两堆的石子数量加起来等于多的那一堆,后手就一定会胜利。所以,Bob把三堆石子的数量分别设为 {k,4k,5k}(k>0)。

现在Alice想要知道,在k 小于 2^n 的时候,有多少种情况先手一定会获得胜利。

输入

一个整数n(1 \le n \le 2 \times 10^91n2×109)。

输出

 输出先手胜利的可能情形数。答案对10^9+7109+7取模。

输出时每行末尾的多余空格,不影响答案正确性

样例输入

3

样例输出

2

题目来源

ACM训练联盟周赛

 

Nim博弈论,当先手为K^(4K)^(5K) == 0时必输。要求先手必赢,让总的数量减去必输的数量就是答案了。当K^(4K)^(5K) == 0时,K^(4K) == 5K,又因为K+2K=5K,所以K的二进制相隔一个位置不能同时为1.

则f(n) = f(n-1) + f(n-3) + f(n-4) + 2,总的数量为2n-1。

有了公式用矩阵快速幂就可以求出来了。

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const ll mod = 1e9+7;
 5 struct mat{
 6     ll m[5][5];
 7     mat() {
 8         memset(m, 0, sizeof(m));
 9     }
10 };
11 
12 mat mul(mat &A, mat &B) {
13     mat C;
14     for(int i = 0; i < 5; i ++) {
15         for(int j = 0; j < 5; j ++) {
16             for(int k = 0; k < 5; k ++) {
17                 C.m[i][j] = (C.m[i][j] + A.m[i][k]*B.m[k][j]) % mod;
18             }
19         }
20     }
21     return C;
22 }
23 
24 mat pow(mat A, ll n) {
25     mat B;
26     for(int i = 0; i < 5; i ++) B.m[i][i] = 1;
27     while(n) {
28         if(n&1) B = mul(B,A);
29         A = mul(A,A);
30         n >>= 1;
31     }
32     return B;
33 }
34 ll pow(ll n) {
35     ll x = 2, y = 1;
36     while(n) {
37         if(n&1) y = (y*x)%mod;
38         x = x*x %mod;
39         n >>= 1;
40     }
41     return y;
42 }
43 int main() {
44     ll n;
45     cin >> n;
46     if(n <= 2) return 0*printf("0\n");
47     else if(n == 3) return 0*printf("2\n");
48     else if(n == 4) return 0*printf("7\n");
49     mat A;
50     A.m[0][0] = A.m[0][2] = A.m[0][3] = A.m[0][4] = 1;
51     A.m[1][0] = A.m[2][1] = A.m[3][2] = A.m[4][4] = 1;
52     A = pow(A, n-4);
53     ll ans = (A.m[0][0]*8+A.m[0][1]*5+A.m[0][2]*3+A.m[0][3]+A.m[0][4]*2+mod)%mod;
54     ans = (pow(n) - ans - 1 + mod) %mod;
55     printf("%lld\n",ans);
56     return 0;
57 }
58 
59 // fn    1 0 1 1 1     fn-1
60 // fn-1  1 0 0 0 0     fn-2
61 // fn-2  0 1 0 0 0     fn-3
62 // fn-3  0 0 1 0 0     fn-4
63 //   2   0 0 0 0 1       2

 

posted @ 2018-07-15 21:46  starry_sky  阅读(555)  评论(0编辑  收藏  举报