[Codeforces] 1182E

Product Oriented Recurrence

矩陣好難:(

題敘

Link
對於 $ x \geq 4 $ , $ f_n = c^{2n-6} f_{n-1} f_{n-2} f_{n-3} $
給定 $ n, f_1, f_2, f_3, c $,求 $ f_n \mod 10^9 + 7 $。

$ ( 4 \leq n \leq 10^{18}, 1 \leq f_1, f_2, f_3, c \leq 10^9 ) $

想法

TLE

用模仿費式數列DP的方法做的話……總複雜度 $ \mathcal{O}(n) $
看一下值域,感覺就是會吃爆TLE……

快速冪

這裡要用到一個技巧:矩陣快速冪
把原本的式子拆開,可以分成兩個部份解決
$ f_n = (c^{2n-6}) \times (f_{n-1} f_{n-2} f_{n-3}) $
先令 $ a_n, b_n, c_n, p_n $ 代表 $ f_n = c^{p_n} f_1^{a_n} f_2^{b_n} f_3^{c_n} $

解決後半($f_1^{a_n} f_2^{b_n} f_3^{c_n}$)

對於這個部分,有:
$\begin{cases} a_n = a_{n-1} + a_{n-2} + a_{n-3}\newline b_n = b_{n-1} + b_{n-2} + b_{n-3} \newline c_n = c_{n-1} + c_{n-2} + c_{n-3} \end{cases}$

所以可以用轉移矩陣來做這件事:
$\begin{bmatrix} {1} & {1} & {1} \newline {1} & {0} & {0} \newline {0} & {1} & {0} \end{bmatrix}\begin{bmatrix} {a_{n-1}} \newline {a_{n-2}} \newline {a_{n-3}} \end{bmatrix}=\begin{bmatrix} {a_{n-1} + a_{n-2} + a_{n-3}} \newline {a_{n-1}} \newline {a_{n-2}} \end{bmatrix}=\begin{bmatrix} {a_{n}} \newline {a_{n-1}} \newline {a_{n-2}} \end{bmatrix}$

結果:我們可以發現這三個共用一個轉移式,只要把左邊的矩陣用快速冪的方式,
就可以$ \mathcal{O}(\log n) $ 內求得 $ a_n, b_n, c_n $

然而,這個方法有溢位的風險,
實作上可以每次做完矩陣乘法 mod $10^9 + 6 $,
原因是因為費馬小定理:
$ a^p \equiv a \mod p $
$ a^{p-1} \equiv 1 \mod p $

再透過一次快速冪,我們可以在$\mathcal{O}(\log n)$內得到$f_{n-1} f_{n-2} f_{n-3}$的部分,
而$c^{2n-6}$呢? 當然也可以!

解決前半($c^{p_n}$)

先列出他的轉移式:
$ p_n = 2n-6 + p_{n-1} + p_{n-2} + p_{n-3} $

和前面不一樣的是,轉移式的前面多了變數,
在轉移矩陣轉移的時候,我們要同時維護$2n-6$,
可是矩陣好像不能每次+2,每次+2……嗎?

在這個部分,可以增開一維,放一個常數$2$進去,
常數本身也很好維護,所以轉移矩陣像這樣:
$ \begin{bmatrix} {1} & {1} & {1} & {1} & {1} \newline {1} & {0} & {0} & {0} & {0} \newline {0} & {1} & {0} & {0} & {0} \newline {0} & {0} & {0} & {1} & {1} \newline {0} & {0} & {0} & {0} & {1} \end{bmatrix} \begin{bmatrix} {p_{n-1}} \newline {p_{n-2}} \newline {p_{n-3}} \newline {2(n-1)-6} \newline {2} \end{bmatrix} = \begin{bmatrix} {p_{n-1} + p_{n-2} + p_{n-3} + 2(n-1)-6 + 2} \newline {p_{n-1}} \newline {p_{n-2}} \newline {2(n-1)-6 + 2} \newline {2} \end{bmatrix} = \begin{bmatrix} {p_{n}} \newline {p_{n-1}} \newline {p_{n-2}} \newline {2n-6} \newline {2} \end{bmatrix}$

結果記得隨時mod,不然會溢位,
再次利用快速冪求得$c^{p_n}$,複雜度$\mathcal{O}(\log n)$

結果

總複雜度$\mathcal{O}(\log n)$
應該過的了,不然就是寫錯了(痛苦的Debug時間)

其他

似乎可以用原根+BSGS過?
不過那個我還沒學過,改天學完再來處理這種方法。

AC Code

快速冪

寫得稍微有點長,不過應該蠻好看懂的(吧?)

  #include <bits/stdc++.h>
  #define ll long long
  #define MOD 1000000007
  using namespace std;

  struct Matrix3
  {
      ll val[3][3] = {{0, 0, 0},
                      {0, 0, 0},
                      {0, 0, 0}};
      Matrix3 operator*(Matrix3 m)
      {
          Matrix3 r;
          for (int i = 0; i < 3; i++)
              for (int j = 0; j < 3; j++)
                  for (int k = 0; k < 3; k++)
                      r.val[i][j] = (r.val[i][j] + this->val[i][k] * m.val[k][j]) % (MOD - 1);
          return r;
      }
  };

  struct Matrix5
  {
      ll val[5][5] = {{0, 0, 0, 0, 0},
                      {0, 0, 0, 0, 0},
                      {0, 0, 0, 0, 0},
                      {0, 0, 0, 0, 0},
                      {0, 0, 0, 0, 0}};
      Matrix5 operator*(Matrix5 m)
      {
          Matrix5 r;
          for (int i = 0; i < 5; i++)
              for (int j = 0; j < 5; j++)
                  for (int k = 0; k < 5; k++)
                      r.val[i][j] = (r.val[i][j] + this->val[i][k] * m.val[k][j]) % (MOD - 1);
          return r;
      }
  };

  Matrix3 A = {{{1, 1, 1},
               {1, 0, 0},
               {0, 1, 0}}};
  Matrix5 B = {{{1, 1, 1, 1, 1},
                {1, 0, 0, 0, 0},
                {0, 1, 0, 0, 0},
                {0, 0, 0, 1, 1},
                {0, 0, 0, 0, 1}}};
  Matrix3 I3 = {{{1, 0, 0},
                {0, 1, 0},
                {0, 0, 1}}};
  Matrix5 I5 = {{{1, 0, 0, 0, 0},
                 {0, 1, 0, 0, 0},
                 {0, 0, 1, 0, 0},
                 {0, 0, 0, 1, 0},
                 {0, 0, 0, 0, 1}}};

  ll f1, f2, f3, c, n, pf1, pf2, pf3, pc;

  Matrix3 fast_pow3(ll n)
  {
      if (n == 0)
          return I3;
      if (n == 1)
          return A;
      if (n % 2)
          return A * fast_pow3(n - 1);

      Matrix3 HA = fast_pow3(n / 2);
      return HA * HA;
  }

  Matrix5 fast_pow5(ll n)
  {
      if (n == 0)
          return I5;
      if (n == 1)
          return B;
      if (n % 2)
          return B * fast_pow5(n - 1);

      Matrix5 HB = fast_pow5(n / 2);
      return HB * HB;
  }

  ll lfast_pow(ll base, ll n)
  {
      if (n == 0)
          return 1;

      if (n == 1)
          return base;

      if (n % 2)
          return (base * lfast_pow(base, n - 1) % (MOD));

      ll half = lfast_pow(base, n / 2);
      return (half * half) % (MOD);
  }

  int main()
  {
      cin >> n >> f1 >> f2 >> f3 >> c;
      Matrix3 M = fast_pow3(n - 3);
      Matrix5 N = fast_pow5(n - 3);
      pf1 = (M.val[0][2]);
      pf2 = (M.val[0][1]);
      pf3 = (M.val[0][0]);
      pc = (2 * N.val[0][4]) % (MOD - 1);
      cout << (((lfast_pow(f1, pf1) * lfast_pow(f2, pf2)) % (MOD)) * (lfast_pow(f3, pf3) * lfast_pow(c, pc) % (MOD))) % (MOD);
  }