题目描述
POJ-3233
看到这令人目瞪口呆的数据范围,
O
(
log
n
)
O(\log n)
O(logn)没逃了,于是,最好想到的肯定是矩阵加速。
前置芝士
问题的简化
首先抛掉矩阵,先想一想这样一道题:
给定
m
,
k
,
x
m,k,x
m,k,x,求
S
k
=
∑
i
=
1
k
x
i
 
m
o
d
 
m
S_k=\sum_{i=1}^{k}x^i\bmod m
Sk=∑i=1kximodm。
这道题可以轻松想出两种对于矩阵加速状态的定义:
[
S
i
x
i
]
\begin{bmatrix}S_i&x^i\end{bmatrix}
[Sixi]或
[
S
i
1
]
\begin{bmatrix}S_i&1\end{bmatrix}
[Si1]。
对于两种状态,都很容易构造出的加速矩阵,如下:
[
1
0
x
x
]
a
n
d
[
x
0
x
1
]
\begin{bmatrix}1&0\\x&x\end{bmatrix}\qquad and\qquad\begin{bmatrix}x&0\\x&1\end{bmatrix}
[1x0x]and[xx01]
于是这道题就AC?了……
本题做法
就和上面的题一样,就是把原先的 x x x变成了矩阵 A A A。
难点1
原先的
x
x
x是要放在一个矩阵中的,那把矩阵
A
A
A还要套在矩阵中吗?怎么套?
这是可以实现的,使用矩阵套矩阵的方法,模板代码如下:
int N, M;
struct Matrix{
int n, m;
long long A[31][31];
inline Matrix(int N = 0, int M = 0) {
n = N; m = M; memset(A, 0, sizeof A);
}
inline Matrix operator * (const Matrix &rhs) const {
Matrix ans = Matrix(n, rhs.m);
for(reg int k = 1; k <= m; k++)
for(reg int i = 1; i <= ans.n; i++)
for(reg int j = 1; j <= ans.m; j++)//据说kij比ijk快(Floyd?)
ans.A[i][j] = ans.A[i][j] + A[i][k] * rhs.A[k][j];
return ans;
}
inline Matrix operator + (const Matrix & rhs) const {
Matrix ans = Matrix(n, m);
for(reg int i = 1; i <= n; i++)
for(reg int j = 1; j <= m; j++)
ans.A[i][j] = (A[i][j] + rhs.A[i][j]) % MOD;
return ans;
}
};
struct Mmatrix{
int n, m;
Matrix A[3][3];
inline Mmatrix(int N = 0, int M = 0) {
n = N; m = M; memset(A, 0, sizeof A);
}
inline Mmatrix operator * (const Mmatrix& rhs) const {
Mmatrix ans = Mmatrix(n, rhs.m);
for(int k = 1; k <= m; k++)
for(int i = 1; i <= ans.n; i++)
for(int j = 1; j <= ans.m; j++) {
if(!ans.A[i][j].n) ans.A[i][j] = Matrix(N, M);
//注意这一步很关键,因为这个时候ans.A[i][j]还是一个0行0列的矩阵
//需要把它赋上行和列的值,才能进行加法运算,否则答案就会为0
ans.A[i][j] = ans.A[i][j] + A[i][k] * rhs.A[k][j];
}
return ans;
}
};
难点2
原先定义的矩阵中是有一个 1 1 1和 0 0 0的,但是到了矩阵中 1 1 1和 0 0 0要变成什么呢?
因为矩阵加速用的是乘法运算,所以令表示
1
1
1的矩阵为
E
E
E,则
E
E
E要做到的就是
E
×
A
E\times A
E×A(
A
A
A为任意矩阵)
=
A
=A
=A。
所以可以轻松的得到
E
E
E是一个对角线的矩阵,又称单位矩阵。
至于
0
0
0的代替物,就是全为
0
0
0的矩阵辣!
代码(你看不到我)
方法1:自动省略input和output
int main() {
ios::sync_with_stdio(false);
cin.tie(); cout.tie();
cin >> N >> k >> MOD;
A.input(N, N);
E = Matrix(N, N);
for(reg int i = 1; i <= N; i++) E.A[i][i] = 1;
O = Matrix(N, N);
f = Mmatrix(1, 2);//S_1, A^1
f.A[1][1] = A; f.A[1][2] = A;
h = Mmatrix(2, 2);
h.A[1][1] = A; h.A[1][2] = O;
h.A[2][1] = E; h.A[2][2] = E;
piow(k - 1);
f.A[1][1].output();
}
方法2:
int main() {
ios::sync_with_stdio(false);
cin.tie(); cout.tie();
cin >> N >> k >> MOD;
A.input(N, N);
E = Matrix(N, N);
for(reg int i = 1; i <= N; i++) E.A[i][i] = 1;
O = Matrix(N, N);
f = Mmatrix(1, 2);
f.A[1][1] = A; f.A[1][2] = E;
h = Mmatrix(2, 2);
h.A[1][1] = A; h.A[1][2] = O;
h.A[2][1] = A; h.A[2][2] = E;
piow(k - 1);
f.A[1][1].output();
}
附:头文件集锦
不打这么多头文件我看不起你!!!
#include<map>
#include<set>
#include<list>
#include<queue>
#include<deque>
#include<stack>
#include<ctime>
#include<vector>
#include<bitset>
#include<cctype>
#include<string>
#include<sstream>
#include<fstream>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<tr1/unordered_map>
#include<tr1/unordered_set>
using namespace std;
using namespace tr1;