这道题可以发现 N 很小,但也没小到那种程度,注意到这题刚刚好能通过 O(N3) 的算法。
而且 M 又这么大……还是递推题……
这就需要矩阵快速幂了。
由于矩阵乘法符合结合律,所以快速幂也适用于矩阵。这题就是一个很好的示例。
我们可以分别对于 N=4,N=5 的情况构造矩阵,然后寻找规律。
任务:构造一个 3×3 的矩阵,使得该矩阵乘以 A3,i−1A2,i−1A1,i−1 得到 A3,iA2,iA1,i.
- 化简结果矩阵,直到每一项可以用原矩阵里面的数乘以一个常数的和表示。
A3,iA2,iA1,i=S×A2,i+T×A3,i−1S×A1,i+T×A2,i−1S×A0,i+T×A1,i−1=T×A3,i−1+S×T×A2,i−1+S2×T×A1,i−1+S3×i T×A2,i−1+S×T×A1,i−1+S2×i T×A1,i−1+S×i
但这里出现了一个问题:i 不是常数,也不能用前面的矩阵表示!
这也不是没有办法,在目标矩阵加两项:i 和 1 就行了!
现在的任务:构造一个 5×5 的矩阵,使得该矩阵乘以 A3,i−1A2,i−1A1,i−1i−11 得到 A3,iA2,iA1,ii1.
- 化简结果矩阵……
A3,iA2,iA1,ii1=T×A3,i−1+S×T×A2,i−1+S2×T×A1,i−1+S3×(i−1)+S3 T×A2,i−1+S×T×A1,i−1+S2×(i−1)+S2 T×A1,i−1+S×(i−1)+S(i−1)+11
OK!
- 解方程!
设我们构造出的矩阵为 a1a2a3a4a5b1b2b3b4b5c1c2c3c4c5d1d2d3d4d5e1e2e3e4e5.
根据矩阵乘法的定义,有:
⎩⎨⎧a1×A3,i+b1×A2,i+c1×A1,i+d1×(i−1)+e1×1=T×A3,i−1+S×T×A2,i−1+S2×T×A1,i−1+S3×(i−1)+S3,a2×A3,i+b2×A2,i+c2×A1,i+d2×(i−1)+e2×1=T×A2,i−1+S×T×A1,i−1+S2×(i−1)+S2,a3×A3,i+b3×A2,i+c3×A1,i+d3×(i−1)+e3×1=T×A1,i−1+S×(i−1)+S,a4×A3,i+b4×A2,i+c4×A1,i+d4×(i−1)+e4×1=i,a5×A3,i+b5×A2,i+c5×A1,i+d5×(i−1)+e5×1=1.
一一对应即可,可以轻松获得方程的解。
最终获得的矩阵为
T0000S×TT000S2×TS×TT00S3S2S10S3S2S11.
对于 N=5 的情况,你们可以自己试一下,最终构造出的矩阵为
T00000S×TT0000S2×TS×TT000S3×TS2×TS×TT00S4S3S2S10S4S3S2S11.
我们发现,最后一行只有最后一列是 1,其余都是 0;第 N 行只有后两列是 1,其余都是 0。
对于第 i(1≤i≤N) 行,第 i 列为 T,其余的均为下一行乘以 S。
构造方法如下:
mtx[n][n] = 1;
mtx[n - 1][n - 1] = mtx[n - 1][n] = 1;
for(int i = n - 2; i >= 0; i--){
mtx[i][i] = t;
for(int j = i + 1; j <= n; j++){
mtx[i][j] = mtx[i + 1][j] * s % mod;
}
}
初始矩阵:
⋮A3,0A2,0A1,001.
然后就可以快速幂了。
矩阵快速幂和普通快速幂差不多,但注意矩阵乘法不满足交换律,所以顺序不能搞混!
#include <iostream>
#include <cstdio>
#include <memory.h>
#define int long long
using namespace std;
const int mod = 998244353;
int n, m, s, t;
int b[35];
int mtx[40][40];
struct matrix{
int a[40][40];
matrix(){
memset(a, 0, sizeof(a));
}
void _1(){
for(int i = 0; i < n - 1; i++) a[n - i - 2][0] = b[i];
a[n][0] = 1;
}
void _2(){
for(int i = 0; i <= n; i++) for(int j = 0; j <= n; j++) a[i][j] = mtx[i][j];
}
matrix operator * (const matrix &b) const{
matrix tmp;
for(int i = 0; i < 35; i++){
for(int j = 0; j < 35; j++){
for(int k = 0; k < 35; k++){
tmp.a[i][j] += a[i][k] * b.a[k][j] % mod;
tmp.a[i][j] %= mod;
}
}
}
return tmp;
}
};
matrix pow(int time){
matrix tmp, a;
tmp._1();
a._2();
while(time){
if(time & 1) tmp = a * tmp;
a = a * a, time >>= 1;
}
return tmp;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> s >> t;
for(int i = 0; i < n - 1; i++) cin >> b[i];
mtx[n][n] = 1;
mtx[n - 1][n - 1] = mtx[n - 1][n] = 1;
for(int i = n - 2; i >= 0; i--){
mtx[i][i] = t;
for(int j = i + 1; j <= n; j++){
mtx[i][j] = mtx[i + 1][j] * s % mod;
}
}
int q;
cin >> q;
while(q--){
int x, y;
cin >> x >> y;
cout << pow(y).a[n - x - 1][0] << '\n';
}
return 0;
}
时间复杂度 O(QN3logM)。
初次推矩阵耗时可能长一些,但熟练之后两三分钟就可以完成了。加油!
有帮助,赞一个