AcWing 1305. GT考試
阿新 • • 發佈:2022-06-06
https://www.acwing.com/solution/content/46059/
一、dp(kmp + 狀態機) + 矩陣乘法
這一題是AcWing 1052. 設計密碼的一道擴充套件題目,分析方式仍然是動態規劃。擴充套件方式是資料量,AcWing 1052. 設計密碼中的\(n\)值最大為\(50\),這裡的\(n\)最大可以取到\(10^9\)。這是一種擴充套件方式,還有另外一種擴充套件方式,不擴充套件\(n\),而是讓不能包含多個字串,對應題目是:AcWing 1053. 修復DNA,可以使用\(AC\)自動機解決。
二、解題思路
通過上面的分析,我們根據狀態計算可以得到第i
層和第i+1
令: $$\large F[i+1]=(f[i+1][0],f[i+1][1],...,f[i+1][m-1])$$
則有:
\[\large F(i+1)=F(i)*A \]展開為:
\[\large \begin{bmatrix} f(i+1,0),f(i+1,1),...,f(i+1,m-1) \end{bmatrix} = \\ \begin{bmatrix} f(i,0),f(i,1),...,f(i,m-1) \end{bmatrix} \times \begin{bmatrix} a_{0,0}& a_{0,1} & ... & a_{0,m-1} \\ a_{1,0}& a_{1,1} & ... & a_{1,m-1} \\ ... \\ a_{m-1,0}& a_{m-1,1} & ... & a_{m-1,m-1} \end{bmatrix}\]根據上面的分析可知,矩陣\(A\)
如何求解陣列\(A\)呢?如果從\(f(i, j)\)可以轉移到\(f(i+1, k)\),則讓\(a[j, k]++\)。即讓\(f(i+1, k) += f(i, j)\):
求出向量\(F(n)\)後,最後的答案就是向量\(F(n)\)中所有的元素之和。
這是一類問題,凡是動態規劃中兩層之間的轉移形式是乘以一個固定矩陣的,都可以使用快速冪優化。
三、實現程式碼
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 25;
int n, m, mod;
char str[N];
int ne[N];
int a[N][N];
// 本題的正解:kmp+狀態機+dp
//矩陣乘法
void mul(int a[][N], int b[][N], int c[][N]) {
int t[N][N] = {0};
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j)
for (int k = 0; k < N; ++k)
t[i][j] = (t[i][j] + (LL)(a[i][k] * b[k][j]) % mod) % mod;
}
memcpy(c, t, sizeof t);
}
int main() {
cin >> n >> m >> mod;
cin >> str + 1;
// kmp
for (int i = 2, j = 0; i <= m; i++) {
while (j && str[j + 1] != str[i]) j = ne[j];
if (str[j + 1] == str[i]) j++;
ne[i] = j;
}
// 初始化A[i][j]
for (int j = 0; j < m; j++)
for (int c = '0'; c <= '9'; c++) {
int k = j;
while (k && str[k + 1] != c) k = ne[k];
if (str[k + 1] == c) k++;
if (k < m) a[j][k]++;
}
// f[0][0]=1 base case
int f[N][N] = {1};
//矩陣快速冪
for (int i = n; i; i >>= 1) {
if (i & 1) mul(f, a, f); // f:基底 a:需要計算快速冪的常數矩陣 f:結果儲存
mul(a, a, a); //倍增a
}
int res = 0;
for (int i = 0; i < m; i++) res = (res + f[0][i]) % mod;
cout << res << endl;
return 0;
}