• 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 imes 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
    ");
    47     else if(n == 3) return 0*printf("2
    ");
    48     else if(n == 4) return 0*printf("7
    ");
    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
    ",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
  • 相关阅读:
    垃圾回收机制,正则模块
    日常模块
    文件路径带有字符串的处理方法
    QT进制之间的相互转换
    4-7 selectors模块
    4-5 异步IO模型
    4-4 多路复用IO模型
    4-3 非阻塞IO
    4-2 阻塞IO
    4-1 IO模型介绍
  • 原文地址:https://www.cnblogs.com/xingkongyihao/p/9315128.html
Copyright © 2020-2023  润新知