• HDU-2802-F(N)


    看到这题讨论版里有说用公式的有说用循环节的,但是个人觉得这两种方法都不靠谱,比赛场上做这种题能直接推出公式需要很强数学功底,而循环节的方法如果循环节比较大就不太好发现了。这种已知通项公式的题还是用矩阵快速幂比较通用,但同是矩阵快速幂,对于这题,也有很大的差异;

    注:memmove这个函数可能不太常见,但还是比较好用的,可以用较低的时间复杂度完成数组的拷贝

    • 方法一
      Time Limit Exceeded 2802 1000MS 1348K 1734 B G++
      #include "bits/stdc++.h"
      using namespace std;
      const int MOD = 2009;
      /* 
       {f(n - 1), f(n - 2), n ^ 3, (n - 1) ^ 3, n ^ 2, n, 1}
       乘MAT得到 
       {f(n), f(n - 1), (n + 1) ^ 3, n ^ 3, (n + 1) ^ 2, n + 1, 1}
      */
      const int MAT[][7] = {
          {0, 1, 1, -1, 0, 0, 0},
          {1, 0, 0, 0, 0, 0, 0},
          {0, 0, 1, 0, 3, 3, 1},
          {0, 0, 1, 0, 0, 0, 0},
          {0, 0, 0, 0, 1, 2, 1},
          {0, 0, 0, 0, 0, 1, 1},
          {0, 0, 0, 0, 0, 0, 1}
      };
      const int TABLE[] = {7, 1, 27, 8, 9, 3, 1};
      struct Mat {
          int mat[7][7];
          Mat() {
              memset(mat, 0, sizeof(mat));
          }
          friend Mat operator * (Mat n, Mat m) {
              Mat res;
              for (int k = 0; k < 7; k++)
              for (int i = 0; i < 7; i++)
              for (int j = 0; j < 7; j++) {
                  res.mat[i][j] += n.mat[i][k] * m.mat[k][j];
                  // 因为当n >=0 时 (n + 1) ^ 3 一定大于 n ^ 3,所以要保证结果是正数; 
                  res.mat[i][j] = (res.mat[i][j] % MOD + MOD) % MOD; 
              }
              return res;
          }
      } m;
      Mat mat_pow(Mat n, int k) {
          Mat res;
          for (int i = 0; i < 7; i++) {
              res.mat[i][i] = 1;
          }
          while (k) {
              if (k & 1) {
                  res = res * n;
              }
              n = n * n;
              k >>= 1;
          }
          return res;
      }
      int main() {
          int n;
          while (scanf("%d", &n) && n) {
              if (n == 1) {
                  puts("1");
                  continue;
              }
              if (n == 2) {
                  puts("7");
                  continue;
              }
              memmove(m.mat, MAT, sizeof(m.mat));
              m = mat_pow(m, n - 2);
              int res = 0;
              for (int i = 0; i < 7; i++) {
                  res = (res + m.mat[0][i] * TABLE[i]) % MOD;
              }
              printf("%d
      ", res);
          }
          return 0;
      }

      这是拿到题直接把递推式拿来套的解法,7层矩阵,超时。于是想到n ^ 3  - (n - 1) ^ 3 = (n - 1) ^ 3 + 3 * (n - 1) ^ 2 + 3 * (n - 1) + 1 - (n - 1) ^ 3;可以抵消(n  - 1) ^ 3;

    • 方法二 

      Accepted 2802 889MS 1384K 1462 B G++
      #include "bits/stdc++.h"
      using namespace std;
      const int MOD = 2009;
      /* 
       {f(n - 1), f(n - 2), n ^ 2, n, 1}
       乘MAT得到 
       {f(n), f(n - 1), (n + 1) ^ 2, n + 1, 1}
      */
      const int MAT[][5] = {
          {0, 1, 3, 3, 1},
          {1, 0, 0, 0, 0},
          {0, 0, 1, 2, 1},
          {0, 0, 0, 1, 1},
          {0, 0, 0, 0, 1} 
      };
      const int TABLE[] = {7, 1, 4, 2, 1};
      struct Mat {
          int mat[5][5];
          Mat() {
              memset(mat, 0, sizeof(mat));
          }
          friend Mat operator * (Mat n, Mat m) {
              Mat res;
              for (int k = 0; k < 5; k++)
              for (int i = 0; i < 5; i++)
              for (int j = 0; j < 5; j++) 
              res.mat[i][j] = (res.mat[i][j] + n.mat[i][k] * m.mat[k][j]) % MOD;
              return res;
          }
      } m;
      Mat mat_pow(Mat n, int k) {
          Mat res;
          for (int i = 0; i < 5; i++) {
              res.mat[i][i] = 1;
          }
          while (k) {
              if (k & 1) {
                  res = res * n;
              }
              n = n * n;
              k >>= 1;
          }
          return res;
      }
      int main() {
          int n;
          while (scanf("%d", &n) && n) {
              if (n == 1) {
                  puts("1");
                  continue;
              }
              if (n == 2) {
                  puts("7");
                  continue;
              }
              memmove(m.mat, MAT, sizeof(m.mat));
              m = mat_pow(m, n - 2);
              int res = 0;
              for (int i = 0; i < 5; i++) {
                  res = (res + m.mat[0][i] * TABLE[i]) % MOD;
              }
              printf("%d
      ", res);
          }
          return 0;
      }

      降到5层矩阵之后能AC掉这题了,但是这个矩阵还不是最好的。这个代码的效率还是不高。

    • 方法三
      Accepted 2802 483MS 1392K 1551 B G++
      #include "bits/stdc++.h"
      using namespace std;
      const int MOD = 2009;
      /* 
       {f(n - 2), (n + 1) ^ 2, (n + 1), 1}
       乘MAT得到 
       {f(n), (n + 3) ^ 2, n + 3, 1}
      */
      const int MAT[][4] = {
          {1, 3, 3, 1},
          {0, 1, 4, 4},
          {0, 0, 1, 2},
          {0, 0, 0, 1} 
      };
      const int TABLE1[] = {1, 4, 2, 1};
      const int TABLE2[] = {7, 9, 3, 1};
      struct Mat {
          int mat[4][4];
          Mat() {
              memset(mat, 0, sizeof(mat));
          }
          friend Mat operator * (Mat n, Mat m) {
              Mat res;
              for (int k = 0; k < 4; k++)
              for (int i = 0; i < 4; i++)
              for (int j = 0; j < 4; j++) 
              res.mat[i][j] = (res.mat[i][j] + n.mat[i][k] * m.mat[k][j]) % MOD;
              return res;
          }
      } m;
      Mat mat_pow(Mat n, int k) {
          Mat res;
          for (int i = 0; i < 4; i++) {
              res.mat[i][i] = 1;
          }
          while (k) {
              if (k & 1) {
                  res = res * n;
              }
              n = n * n;
              k >>= 1;
          }
          return res;
      }
      int main() {
          int n;
          while (scanf("%d", &n) && n) {
              if (n == 1) {
                  puts("1");
                  continue;
              }
              if (n == 2) {
                  puts("7");
                  continue;
              }
              memmove(m.mat, MAT, sizeof(m.mat));
              m = mat_pow(m, n - 1 >> 1);
              int res = 0;
              /*
                假设重定义两个函数,a(n) = f(2 * n - 1), b(n) = f(2 * n); 
                if (n & 1) 求得a((n - 1) >> 1)即为 f(n) 
                else 求得b((n - 1) >> 1) 即为f(n) 
              */ 
              if (n & 1) {
                  for (int i = 0; i < 4; i++) {
                      res = (res + m.mat[0][i] * TABLE1[i]) % MOD;
                  }
              } else {
                  for (int i = 0; i < 4; i++) {
                      res = (res + m.mat[0][i] * TABLE2[i]) % MOD;
                  }
              }
              printf("%d
      ", res);
          }
          return 0;
      }

      递推式是从f(n)直接到f(n + 2)的,所以相当于把f拆成两个函数分开讨论,只用4层矩阵就够了。对于矩阵快速幂,4层应该是最少的了。如果还要再快,那就只能采取循环节或公式的方式了。

    • 方法四(来自讨论版,代码省略)
      这题的循环节是4018(正好是MOD的两倍,不知道是不是巧合)。
    • 方法五(来自讨论版,代码省略)
      n为奇数 (n+1)(n+1)(2n-1)/4;
      n为偶数 n*n*(2n+3)/4
  • 相关阅读:
    SQL语句导入导出大全
    针对SQL INJECTION的SQL SERVER安全设置初级篇
    3389输入法漏洞————入侵动画
    asp的Server对象
    网络安全防范体系及设计原则
    ARP、Tracert、Route 与 NBTStat命令详解
    VC编译器使用Matlab_Com组件的方法
    ASP与ActiveX控件交互实战(二)
    ASP 嵌入式Web视频点播系统实现方法
    在ASP程序设计中在使用Response对象
  • 原文地址:https://www.cnblogs.com/Angel-Demon/p/10403722.html
Copyright © 2020-2023  润新知