jx day2 总结
根本没有进阶啊,我感觉和昨天昨天知识没啥关系。。。
就是个矩阵乘法讲了一上午,后面题目挺难。。。
先讲讲矩阵是咋乘的
矩阵乘法的必要条件是第一个矩阵的列数要等于第二个矩阵的行数
就是一个 n ∗ m n*m n∗m大小的矩阵乘以一个 m ∗ p m*p m∗p大小的矩阵会得到一个 n ∗ p n*p n∗p的矩阵
显然如果一个 1 ∗ n 1*n 1∗n的向量乘以一个 n ∗ n n*n n∗n的方阵,得到的依然是一个 1 ∗ n 1*n 1∗n的向量
设答案矩阵 a n s ans ans,则 a n s i j = ∑ k = 1 m a i k ∗ b k j ans_{ij}=\sum\limits_{k=1}^ma_{ik}*b_{kj} ansij=k=1∑maik∗bkj
众所周知,矩阵满足结合律和分配律,但是不满足交换律,即
A
∗
B
A*B
A∗B不一定等于
B
∗
A
B*A
B∗A
矩阵的幂被定义成一个矩阵自己称自己 k k k次,例如 A k = A ∗ A ∗ . . . ∗ A A^k=A*A*...*A Ak=A∗A∗...∗A
那么因为矩阵满足结合律,所以我们可以通过快速幂加速矩阵乘法
于是,矩阵可以加速递推
那最朴实的斐波那契数列举例: f i = f i − 1 + f i − 2 f_i=f_{i-1}+f_{i-2} fi=fi−1+fi−2
当 n n n为 1 0 9 10^9 109或更大的时候,我们怎么办呢?
我们发现, f i f_i fi仅与它前两项有关,所以我们构造一个一维的向量 [ a 1 a 0 ] \begin{bmatrix} a_1 \\ a_0 \\ \end{bmatrix} [a1a0] (其中 a 1 > a 0 a_1>a_0 a1>a0)
如何能够让他做一次操作使得它变成 [ a 1 + a 0 a 1 ] \begin{bmatrix} a_1+a_0 \\ a_1 \\ \end{bmatrix} [a1+a0a1] ?
当然,我们是构造一个2*2的矩阵
通过凑的方法我们发现这个方阵可以为
[
1
1
1
0
]
\begin{bmatrix} 1&1 \\ 1&0 \\ \end{bmatrix}
[1110],我们称之为单位矩阵
于是按照管用套路,设向量为 A A A,单位矩阵为 B B B,则 A N = A ∗ B N A^N=A*B^N AN=A∗BN
通过矩阵快速幂能够优化到 O ( 2 3 l o g n ) O(2^3logn) O(23logn)
但是有些读者就会问,这个单位矩阵怎么处理啊?
如此,我们寻找通法
对于一个普通递推式 f i = a 1 ∗ f i − 1 + a 2 ∗ f i − 2 + a 3 ∗ f i − 3 + . . . + a k ∗ f i − k f_i=a_1*f_{i-1}+a_2*f_{i-2}+a_3*f_{i-3}+...+a_k*f_{i-k} fi=a1∗fi−1+a2∗fi−2+a3∗fi−3+...+ak∗fi−k
可构造一个一维的向量 [ f i f i − 1 . . . f 1 ] \begin{bmatrix} f_i \\ f_{i-1} \\ ... \\ f_1 \\ \end{bmatrix} ⎣⎢⎢⎡fifi−1...f1⎦⎥⎥⎤
那么单位矩阵就是 [ a 1 a 2 a 3 . . . a k − 1 a k 1 0 0 . . . 0 0 0 1 0 . . . 0 0 . . . . 0 0 0 . . . 1 0 ] \begin{bmatrix} a_1 & a_2 & a_3 & ... & a_{k-1} & a_k \\ 1 & 0 & 0 & ... & 0 & 0 \\ 0 & 1 & 0 & ... & 0 & 0 \\ & &.... & \\ 0 & 0 & 0 & ... & 1 & 0 \\ \end{bmatrix} ⎣⎢⎢⎢⎢⎡a1100a2010a300....0............ak−1001ak000⎦⎥⎥⎥⎥⎤,正对角线下的那条对角线为 1 1 1,其余除第一行外的节点均为 0 0 0
当然,我们发现矩阵能够优化的递推基本都有一个性质,就是都是线性齐次递推,从 a k ∗ f i − k a_k*f_{i-k} ak∗fi−k发现 k + i − k = i k+i-k=i k+i−k=i,和是相同的,是一个比较简单的判定方法
矩阵乘法这破玩野有什么用?
洛谷上有个模板题可以去做一下
一般出在省选用来优化递推的(而且都很恶心
举个例子 GT考试
不会KMP的同学自动跳过吧
n有 1 0 9 10^9 109,直接想好想很困难,试图先想个暴力?
我们能够很轻松的想到一个暴力:
f [ i ] [ j ] f[i][j] f[i][j]表示长串匹配到第 i i i位,短串最多可以匹配到第 j j j位的方案数
明显 a n s = ∑ i = 1 m − 1 f [ n ] [ i ] ans=\sum\limits_{i=1}^{m-1}f[n][i] ans=i=1∑m−1f[n][i]
我们可以轻松YY出方程:
f
[
i
]
[
j
]
=
∑
k
=
0
m
−
1
f
[
i
−
1
]
[
k
]
∗
g
[
k
]
[
j
]
f[i][j]=\sum\limits_{k=0}^{m-1}f[i-1][k]*g[k][j]
f[i][j]=k=0∑m−1f[i−1][k]∗g[k][j],其中
g
[
k
]
[
j
]
g[k][j]
g[k][j]表示
k
k
k以前的串中与模式串1到
j
j
j位匹配的位置数(感觉自己没说清),很明显通过KMP预处理就行
时间复杂度 O ( n ∗ m 2 ) O(n*m^2) O(n∗m2),TLE
如何优化?
想n这么大,连数组都开不下的题目,我们应当马上想到矩乘优化
我们发现, g [ i ] [ j ] g[i][j] g[i][j]是固定的,可以把 f [ i ] [ j ] f[i][j] f[i][j]当成一个矩阵, F [ i ] F[i] F[i]的第 i i i行的元素就是 f [ i ] [ j ] f[i][j] f[i][j]
F [ i ] = F [ i − 1 ] ∗ G F[i]=F[i-1]*G F[i]=F[i−1]∗G, G G G为固定值,矩乘优化基本式,随便做
int n, m, MOD ;
int nxt[N], match[N][50] ;
char s[N] ;
class matrix {
public:
int a[50][50] ;
matrix() {
clr(a) ;
}
matrix operator *(matrix b) {
matrix res ;
clr(res.a) ;
rep(i, 0, m - 1)
rep(j, 0, m - 1)
rep(k, 0, m - 1) {
res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % MOD ;
}
return res ;
}
} f, g ;
inline matrix ksm(matrix A, int pw) {
matrix ans ;
rep(i, 0, m) ans.a[i][i] = 1 ;
for (; pw; pw >>= 1, A = A * A) if (pw & 1) ans = ans * A ;
return ans ;
}
signed main(){
scanf("%d%d%d", &n, &m, &MOD) ;
scanf("%s", s + 1) ;
nxt[0] = -1 ;
rep(i, 1, m) {
int j = nxt[i - 1] ;
while (j != -1 && s[j + 1] != s[i]) j = nxt[j] ;
nxt[i] = j + 1 ;
}
nxt[0] = 0 ;
rep(i, 0, m - 1)
rep(j, '0', '9') {
int tmp = i ;
while (s[tmp + 1] != j && tmp) tmp = nxt[tmp] ;
if (s[tmp + 1] == j) tmp++ ;
if (tmp < m) match[i][tmp]++ ;
}
clr(f.a) ; clr(g.a) ;
f.a[0][0] = 1 ;
rep(i, 0, m)
rep(j, 0, m)
g.a[i][j] = match[i][j] ;
g = ksm(g, n) ;
f = f * g ;
int ans = 0 ;
rep(i, 0, m - 1) ans = (ans + f.a[0][i]) % MOD ;
printf("%d\n", ans) ;
return 0 ;
}
再看一个题[HNOI2011]数学作业
O(n)算法很显然,但是钦定TLE
显然 d p dp dp方程为 f [ i ] = f [ i − 1 ] ∗ 1 0 k + i f[i]=f[i-1]*10^k+i f[i]=f[i−1]∗10k+i
显然就是矩乘优化了啊
但是 1 0 k 10^k 10k这玩野怎么搞啊
当然可以,我们枚举位数,从 1 1 1位数枚举到 n n n的位数,对每一个位数进行矩阵快速幂。
有可能不是整的,处理一下就好了
ll n, MOD ;
struct mat {
ll a[5][5] ;
mat operator = (int k) {
a[1][1] = 1 ;
rep(i, 1, k) a[1][1] *= 10 ;
a[1][1] %= MOD ;
a[1][2] = a[1][3] = 0 ;
a[2][1] = a[2][2] = 1 ; a[2][3] = 0 ;
a[3][1] = a[3][2] = a[3][3] = 1 ;
return *this ;
}
mat operator * (mat b) {
mat c ;
rep(i, 1, 3)
rep(j, 1, 3) {
c.a[i][j] = 0 ;
rep(k, 1, 3)
c.a[i][j] = (c.a[i][j] + a[i][k] % MOD * b.a[k][j] % MOD) % MOD ;
}
return c ;
}
mat operator ^(ll x) {
mat c, a ; a = *this ;
rep(i, 1, 3)
rep(j, 1, 3)
c.a[i][j] = (i == j) ;
for (; x; x >>= 1, a = a * a) if (x & 1) c = c * a ;
return c ;
}
} a[20], b ;
ll pow10(int x) {
ll ans = 1 ;
rep(i, 1, x) ans *= 10 ;
return ans ;
}
signed main(){
freopen("concatenate.in", "r", stdin) ;
freopen("concatenate.out", "w", stdout) ;
scanf("%lld%lld", &n, &MOD) ;
int k = 0 ; ll tmp = n ;
while (tmp) k++, tmp /= 10 ;
rep(i, 1, k - 1) {
a[i] = i ;
a[i] = a[i] ^ (pow10(i - 1) * 9) ;
}
tmp = n - pow10(k - 1) + 1 ;
a[k] = k ;
a[k] = a[k] ^ tmp ;
rep(i, 1, 3)
rep(j, 1, 3)
b.a[i][j] = (i == j) ;
rep(i, 1, k) b = b * a[i] ;
printf("%lld\n", b.a[3][1]) ;
return 0 ;
}
没了就AC了这两道
之后讲一讲如何把矩阵快速幂优化到 O ( n 2 l o g n ) O(n^2logn) O(n2logn)
特征多项式
裸题 BZOJ 4161 Shlw loves matrixI
int n, k;
int a[MAXN], now[MAXN];
int res[MAXN], h[MAXN];
void times(int *x, int *y) {
static int tmp[MAXN];
memset(tmp, 0, sizeof(tmp));
for (int i = 0; i <= k - 1; i++)
for (int j = 0; j <= k - 1; j++)
tmp[i + j] = (tmp[i + j] + 1ll * x[i] * y[j]) % P;
for (int i = 2 * k - 2; i >= k; i--) {
for (int j = 1; j <= k; j++)
tmp[i - j] = (tmp[i - j] + 1ll * tmp[i] * a[j]) % P;
tmp[i] = 0;
}
for (int i = 0; i <= k - 1; i++)
x[i] = tmp[i];
}
int main() {
read(n), read(k);
for (int i = 1; i <= k; i++) {
read(a[i]);
if (a[i] < 0) a[i] += P;
}
for (int i = 0; i <= k - 1; i++) {
read(h[i]);
if (h[i] < 0) h[i] += P;
}
if (n <= k - 1) {
writeln(h[k]);
return 0;
}
n -= k - 1; res[0] = now[1] = 1;
while (n != 0) {
if (n & 1) times(res, now);
n >>= 1;
times(now, now);
}
for (int i = k; i <= 2 * k - 2; i++)
for (int j = 1; j <= k; j++)
h[i] = (h[i] + 1ll * h[i - j] * a[j]) % P;
int ans = 0;
for (int i = 0; i <= k - 1; i++)
ans = (ans + 1ll * h[k - 1 + i] * res[i]) % P;
writeln(ans);
return 0;
}