# Q老師度假（變形矩陣快速冪優化DP）

### Sample input

3 2
0 1
1 0
4 3
1 2 3
1 2 3
1 2 3


### Sample output

2
9


### 解題思路

$f[i][j]=max(f[i-1][k]+H[k][j]), 1\le k\le M \tag{1}$

$f[i][j]=\sum_{k=1}^{M}(f_2[i][k]\times f_3[k][j]) \tag{2}$
$f_2$矩陣中的$f_2[i][k]$看作$f[i-1][k]$，將$f_3$矩陣中的$f_3[k][j]$看作$H[k][j]$。我們可以得到：
$f[i][j]=\sum_{k=1}^{M}(f[i-1][k]\times H[k][j])\tag{3}$

\begin{aligned} ((AB)C)[i,j] &=\max_{l=1}^{c}((AB)[i,l]+C[l,j])\\ &=\max_{l=1}^{c}(\max_{k=1}^{b}(A[i,k]+B[k,l])+C[l,j])\\ &=\max_{l=1}^{c}\max_{k=1}^{b}(A[i,k]+B[k,l]+C[l,j])\\ &=\max_{k=1}^{b}\max_{l=1}^{c}(A[i,k]+B[k,l]+C[l,j])\\ &=\max_{k=1}^{b}(A[i,k]+\max_{l=1}^{c}(B[k,l]+C[l,j]))\\ &=\max_{k=1}^{b}(A[i,k]+BC[k,j])\\ &=(A(BC))[i,j] \end{aligned}

$ans[i]=\begin{bmatrix} f[i][1]\\ \vdots\\ f[i][M] \end{bmatrix}= \begin{bmatrix} H[1][1]&\cdots&H[M][1]\\ \vdots&\ddots&\vdots\\ H[1][M]&\cdots&H[M][M] \end{bmatrix}* \begin{bmatrix} f[i-1][1]\\ \vdots\\ f[i-1][M] \end{bmatrix}$

### 完整代碼

//#pragma GCC optimize(2)
//#pragma G++ optimize(2)
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

struct Matrix{
Matrix(int _size=0) {
Size=_size; mat=new long long*[Size];
for (int i=0; i<Size; i++) mat[i]=new long long[Size];
for (int i=0; i<Size; i++)
for (int j=0; j<Size; j++)
mat[i][j]=0;
}
Matrix(const Matrix& t){
Size=t.Size; mat=new long long*[Size];
for (int i=0; i<Size; i++) mat[i]=new long long[Size];
memcpy(mat,t.mat,sizeof(mat));
}
~Matrix(){
for (int i=0; i<Size; i++) delete[] mat[i];
delete[] mat;
}
Matrix operator*(const Matrix& t) const{//僞.乘法
Matrix ret(Size);
for (int i=0; i<Size; ++i)
for (int j=0; j<Size; ++j)
for (int k=0; k<Size; ++k)
ret.mat[i][j]=max(ret.mat[i][j],mat[i][k]+t.mat[k][j]);
return ret;
}
Matrix operator%(const int m) const {
Matrix ret(Size);
for (int i=0; i<Size; i++)
for (int j=0; j<Size; j++)
ret.mat[i][j]=mat[i][j]%m;
return ret;
}
Matrix& operator=(const Matrix& t){
for (int i=0; i<Size; i++) delete[] mat[i];
delete[] mat;
Size=t.Size;
mat=new long long*[Size];
for (int i=0; i<Size; i++) mat[i]=new long long[Size];
for (int i=0; i<Size; i++)
for (int j=0; j<Size; j++)
mat[i][j]=t.mat[i][j];
return *this;
}
void quick_pow(int x){
Matrix ret(Size);
//for (int i=0; i<Size; i++) ret.mat[i][i]=1;//注意，乘法含義改變，單位矩陣全0
while(x){
if(x&1) {
ret=ret*(*this);
}
*this=(*this)*(*this);
x>>=1;
}
*this=ret;
}
void output(){
printf("Size: %d\n",Size);
for (int i=0; i<Size; i++){
for (int j=0; j<Size; j++){
printf("%lld ",mat[i][j]);
}
printf("\n");
}
}
int Size;
long long** mat;
};
int getint(){
int x=0,s=1; char ch=' ';
while(ch<'0' || ch>'9'){ ch=getchar(); if(ch=='-') s=-1;}
while(ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar();}
return x*s;
}
int n,m;
int main(){
//ios::sync_with_stdio(false);
//cin.tie(0);
while(scanf("%d %d",&n,&m)!=EOF){
Matrix mat1(m);
for (int i=0; i<m; i++)
for (int j=0; j<m; j++)
scanf("%lld",&mat1.mat[i][j]);

mat1.quick_pow(n-1);
long long ans=0;
for (int i=0; i<m; i++)
for (int j=0; j<m; j++)
ans=max(ans,mat1.mat[i][j]);
//mat1.output();
printf("%lld\n",ans);
}
return 0;
}