[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} \\ b_n = b_{n-1} + b_{n-2} + b_{n-3} \\ c_n = c_{n-1} + c_{n-2} + c_{n-3} \end{cases} $$

所以可以用轉移矩陣來做這件事:

$$ \begin{bmatrix} {1} & {1} & {1} \\ {1} & {0} & {0} \\ {0} & {1} & {0} \end{bmatrix} \begin{bmatrix} {a_{n-1}} \\ {a_{n-2}} \\ {a_{n-3}} \end{bmatrix} = \begin{bmatrix} {a_{n-1} + a_{n-2} + a_{n-3}} \\ {a_{n-1}} \\ {a_{n-2}} \end{bmatrix} = \begin{bmatrix} {a_{n}} \\ {a_{n-1}} \\ {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 \Longrightarrow 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} \\ {1} & {0} & {0} & {0} & {0} \\ {0} & {1} & {0} & {0} & {0} \\ {0} & {0} & {0} & {1} & {1} \\ {0} & {0} & {0} & {0} & {1} \end{bmatrix} \begin{bmatrix} {p_{n-1}} \\ {p_{n-2}} \\ {p_{n-3}} \\ {2(n-1)-6} \\ {2} \end{bmatrix} = \begin{bmatrix} {p_{n-1} + p_{n-2} + p_{n-3} + 2(n-1)-6 + 2} \\ {p_{n-1}} \\ {p_{n-2}} \\ {2(n-1)-6 + 2} \\ {2} \end{bmatrix} = \begin{bmatrix} {p_{n}} \\ {p_{n-1}} \\ {p_{n-2}} \\ {2n-6} \\ {2} \end{bmatrix} $$

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

結果

總複雜度 $\mathcal{O}(\log n)$。

AC Code

快速冪

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

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
  #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);
  }