先贴模板:
矩阵乘法模板
struct Matrix{
int row, col;
LL m[MAXN][MAXN];
void init(int row, int col){
this->row = row;this->col = col;
for (int i = 0; i < row; ++i)
for (int j = 0; j < col; ++j)
m[i][j] = 0;
}
}C,I; //输入和单位矩阵,单位矩阵初始化:I.m[i][j] = (i == j) ? 1 : 0;
Matrix operator*(const Matrix & a, const Matrix& b){
Matrix res;
res.init(a.row, b.col);
for (int k = 0; k < a.col; ++k){
for (int i = 0; i < res.row; ++i){
if (a.m[i][k] == 0) continue;
for (int j = 0; j < res.col; ++j){
if (b.m[k][j] == 0) continue;
res.m[i][j] = (a.m[i][k] * b.m[k][j] + res.m[i][j]) % mod;
}
}
}
return res;
}
Matrix operator+(const Matrix & a, const Matrix& b){
Matrix res;
res.init(a.row, b.col);
for (int i = 0; i< a.col; ++i){
for (int j = 0; j < res.row; ++j)
{ res.m[i][j] = (a.m[i][j] + b.m[i][j] ) % mod;}
}
return res;
}
Matrix pow(Matrix A, LL n){
Matrix ans = I, p = A;
while (n){
if (n & 1){ ans = ans*p; n--;}
n >>= 1; p = p*p;
}
return ans;
}
经典题目1 :给定n个点,m个操作,构造O(m+n)的算法输出m个操作后各点的位置。操作有平移、缩放、翻 转和旋转
这里的操作是对所有点同时进行的。其中翻转是以坐标轴为对称轴进行翻转(两种情况),旋转则以原点为中心。如果对每个点分别进行模拟,那么m个操作总共耗时O(mn)。利用矩阵乘法可以在O(m)的时间里把所有操作合并为一个矩阵,然后每个点与该矩阵相乘即可直接得出最终该点的位置,总共耗时O(m+n)。假设初始时某个点的坐标为x和y,下面5个矩阵可以分别对其进行平移、旋转、翻转和旋转操作。预先把所有m个操作所对应的矩阵全部乘起来,再乘以(x,y,1),即可一步得出最终点的位置。
经典题目 2 给定矩阵A,请快速计算出A^n(n个A相乘)的结果,输出的每个数都mod p
《快速幂模板》
经典题目 3 给定矩阵A,求A + A^2 + A^3 + … + A^k的结果(两个矩阵相加就是对应位置 分别相加。输出数据mod m,k<=10^9
Matrix sum(Matrix A, int k) //二分求(A+A^1+A^2+...A^k)%mod
{
if (k == 1) return A;
Matrix t = sum(A, k / 2);
if (k & 1)
{
Matrix cur = pow(A, k / 2 + 1); t = t + (t*cur); t = t + cur;
}
else
{
Matrix cur = pow(A, k / 2); t = t + (t*cur);
}
return t;
}
经典题目 4 题目大意:顺次给出m个置换,反复使用这m个置换对初始序列进行操作,问k次置换后的序列。M<=10,k<2^31
例题:VOJ 1049 注意理清楚到达谁乘谁,不同的初始化对应不同的乘法次序
Matrix temp[15], ans; //操作矩阵,结果
int main(){
int N, M, K, num;
while (scanf("%d%d%d", &N, &M, &K) != EOF){
I.init(N, N); C.init(N, 1); //初始化单位矩阵I和初始矩阵C。
for (int i = 0; i < N; i++){
I.m[i][i] = 1; C.m[i][0] = i+1;
}
ans = I;
for (int i = 0; i < M; i++) //对于每一行操作。转换成一个n*n的操作矩阵。
{
temp[i].init(N, N);
for (int j = 0; j < N; j++){
scanf("%d", &num); temp[i].m[j][num-1] = 1;
}
}
for (int i = 0; i < M; i++) //M个操作相乘为一个循环后的操作矩阵
{
ans = temp[i]*ans;
}
int div = K / M; //需要循环多少次
int rem = K%M; //剩下需要模拟多少次
ans = pow(ans, div); //矩阵快速幂
for (int i = 0; i < rem; i++) //模拟最后的求余次
{ ans = temp[i] * ans; }
ans = ans*C; //最终结果矩阵*初始矩阵=结果
for (int i = 0; i < N; i++){
printf("%d", ans.m[i][0]);
if (i != N - 1) { printf(" "); }
Else { printf("\n"); }
}
}
}
经典题目5 《算法艺术与信息学竞赛》207页(2.1代数方法和模型,[例题5]细菌,版次不同可能页码有偏差)
经典问题6 给定n和p,求第n个Fibonacci数mod p的值,n不超过 2^31
<<挑战程序设计竞赛 P199>>
经典问题7 例题:VOJ 1067
求出任何一个线性递推式的第n项,其对应矩阵的构造方法为: 在右上角的(n-1)*(n-1)的小矩阵中的主对角线上填 1,矩阵第n行填对应的系数,其它地方都填 0例如,我们可以用下面的矩阵乘法来二分计算f(n) = 4f(n-1) - 3f(n-2) + 2f(n-4)的第k项:
Matrix temp;
int main(){
LL n; int k;
while (cin>>k>>n){
C.init(k, k); I.init(k, k);
for (int i = 0; i < k; i++){
C.m[i][0] = 1;
for (int j = 0; j < i; j++){
C.m[i][0] += C.m[j][0];
}
I.m[i][i] = 1;
}
temp.init(k, k);
for (int i = 0; i < k; i++){
if (i < k-1){ //右上角矩阵
temp.m[i][i+1] = 1;
}
temp.m[k - 1][i] = 1; //最后一行,此题系数为1
}
temp = pow(temp, n - 1);
C = temp*C;
cout << C.m[0][0]%mod << endl;
}
}
经典问题 8 给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod <<挑战程序竞赛 P203>>
经典题目 9 用 1 x 2 的多米诺骨牌填满M x N的矩形有多少种方案,M<=5,N<2^31,输出 答案mod p的结果
#define MAXN 258
#define MOD 9937
#define size (1<<M)
int N, M;
int ans[1200];
class Matrix{
public:
int mt[MAXN][MAXN];
Matrix Multiply(Matrix);
Matrix Add(Matrix);
Matrix quickpower(int);
}AA;
Matrix Matrix::Add(Matrix A){
Matrix C;
for( int i = 1; i <= size; i++)
for( int j = 1; j <= size; j++)
C.mt[i][j] = mt[i][j] + A.mt[i][j];
return C;
}
Matrix Matrix::Multiply(Matrix A){
Matrix C;
for( int i = 0; i < size; i++) {
for( int j = 0; j < size; j++){
int sum = 0;
for( int k = 0; k < size; k++) {
if( A.mt[i][k] && mt[k][j] ){
sum += (A.mt[i][k] % MOD) * (mt[k][j] % MOD);
sum %= MOD;
}
}
C.mt[i][j] = sum;
}
}
return C;
}
Matrix Matrix::quickpower(int n){
Matrix B;
B = *this;
while( n > 0 ) {
if( n & 1 )
*this = (*this).Multiply(B);
n = n / 2;
B = B.Multiply(B);
}
return *this;
}
void pre(int x, int state){//横的摆放状态
if( x > M )
return;
if( x == M ) {
ans[state] = 1;
return;
}
pre(x + 1, state << 1 );
pre(x + 2, 3 | (state << 2) );
}
int main( ){
while( scanf("%d%d",&N,&M) != EOF ){
if( N < M )
swap(N,M);
memset(ans, 0, sizeof(ans));
pre(0, 0);
if( N % 2 == 1 && M % 2 == 1 ){
puts("0"); continue;
}
for( int i = 0; i < (1<<M); i++){
for( int j = 0; j < (1<<M); j++){
AA.mt[i][j] = 0;
if( ((~i)&j) == ((~i)&((1<<M)-1)) )
{ AA.mt[i][j] = ans[i&j]; }
}
}
AA = AA.quickpower(N-1);
printf("%d\n",AA.mt[(1<<M)-1][(1<<M)-1]);
}
}
经典题目 10 题目大意是,检测所有可 的DNA只能由ACTG四个字符构成。题目将给出 10 个以内的病毒片段, 能的n位DNA串有多少个DNA串中不含有指定的病毒片段。合法 每个片段长度不超10, 数据规模n<=2000000000
struct Matrix{
unsigned long long mat[40][40];
int n;
Matrix(){}
Matrix(int _n){
n = _n;
for (int i = 0; i<n; i++)
for (int j = 0; j<n; j++)
mat[i][j] = 0;
}
Matrix operator *(const Matrix &b)const{
Matrix ret = Matrix(n);
for (int i = 0; i<n; i++)
for (int j = 0; j<n; j++)
for (int k = 0; k<n; k++)
ret.mat[i][j] += mat[i][k] * b.mat[k][j];
return ret;
}
};
unsigned long long pow_m(unsigned long long a, int n){
unsigned long long ret = 1;
unsigned long long tmp = a;
while (n)
{
if (n & 1)ret *= tmp;
tmp *= tmp;
n >>= 1;
}
return ret;
}
Matrix pow_M(Matrix a, int n){
Matrix ret = Matrix(a.n);
for (int i = 0; i<a.n; i++)
ret.mat[i][i] = 1;
Matrix tmp = a;
while (n){
if (n & 1)ret = ret*tmp;
tmp = tmp*tmp;
n >>= 1;
}
return ret;
}
struct Trie{
int next[40][26], fail[40];
bool end[40];
int root, L;
int newnode(){
for (int i = 0; i < 26; i++)
next[L][i] = -1;
end[L++] = false;
return L - 1;
}
void init(){L = 0; root = newnode();}
void insert(char buf[]){
int len = strlen(buf);
int now = root;
for (int i = 0; i < len; i++){
if (next[now][buf[i] - 'a'] == -1)
next[now][buf[i] - 'a'] = newnode();
now = next[now][buf[i] - 'a'];
}
end[now] = true;
}
void build(){
queue<int>Q;
fail[root] = root;
for (int i = 0; i < 26; i++)
if (next[root][i] == -1)
next[root][i] = root;
else
{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
while (!Q.empty())
{
int now = Q.front();
Q.pop();
if (end[fail[now]])end[now] = true;
for (int i = 0; i < 26; i++)
if (next[now][i] == -1)
next[now][i] = next[fail[now]][i];
else
{
fail[next[now][i]] = next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
Matrix getMatrix()
{
Matrix ret = Matrix(L + 1);
for (int i = 0; i < L; i++)
for (int j = 0; j < 26; j++)
if (end[next[i][j]] == false)
ret.mat[i][next[i][j]] ++;
for (int i = 0; i < L + 1; i++)
ret.mat[i][L] = 1;
return ret;
}
void debug()
{
for (int i = 0; i < L; i++)
{
printf("id = %3d,fail = %3d,end = %3d,chi = [", i, fail[i], end[i]);
for (int j = 0; j < 26; j++)
printf("%2d", next[i][j]);
printf("]\n");
}
}
};
char buf[10];
Trie ac;
int main(){
int n, L;
while (scanf("%d%d", &n, &L) == 2){
ac.init();
for (int i = 0; i < n; i++)
{ scanf("%s", buf); ac.insert(buf); }
ac.build();
Matrix a = ac.getMatrix(); a = pow_M(a, L);
unsigned long long res = 0;
for (int i = 0; i < a.n; i++)
res += a.mat[0][i];
res--;
/*
* f[n]=1 + 26^1 + 26^2 +...26^n
* f[n]=26*f[n-1]+1
* {f[n] 1} = {f[n-1] 1}[26 0;1 1]
* 数是f[L]-1;
* 此题的L<2^31.矩阵的幂不能是L+1次,否则就超时了
*/
a = Matrix(2);
a.mat[0][0] = 26; a.mat[1][0] = a.mat[1][1] = 1; a = pow_M(a, L);
unsigned long long ans = a.mat[1][0] + a.mat[0][0];
ans--; ans -= res;
cout << ans << endl;
}
}