【vijos1067】【矩陣乘法】守望者的煩惱
阿新 • • 發佈:2019-01-29
很經典的一道動態規劃題目。
方程為:f[i] = ∑f[i-j] j∈[1,k]
這是一個線性遞推式,任意的線性遞推式都可以使用矩陣乘法來加速。
“我們可以用上面的方法二分求出任何一個線性遞推式的第n項,其對應矩陣的構造方法為:在右上角的(n-1)*(n-1)的小矩陣中的主對角線上填1,矩陣第n行填對應的係數,其它地方都填0。例如,我們可以用下面的矩陣乘法來二分計算f(n) = 4f(n-1) - 3f(n-2) + 2f(n-4)的第k項”(轉自Matrix67):
這道題n的範圍很大,所以可以使用矩陣乘法來優化。
只要構造出一個符合題意的矩陣即可。
程式碼:
#include<cstdio> #include<cstring> using namespace std; const int mo = 7777777; const int maxn = 20; struct Matrix { long long v[maxn][maxn]; int x,y; Matrix() { memset(v,0,sizeof(v)); x = y = 0; } }op,f; long long n,k; void init() { freopen("vijos1067.in","r",stdin); freopen("vijos1067.out","w",stdout); } void readdata() { scanf("%lld%lld",&k,&n); } Matrix mtMul(Matrix A,Matrix B) { if(!A.x || !A.y)return B; Matrix C; C.x = A.x;C.y = B.y; for(int i = 1;i <= A.x;i++) { for(int j = 1;j <= B.y;j++) { for(int k = 1;k <= A.y;k++) { C.v[i][j] = (A.v[i][k] * B.v[k][j] + C.v[i][j]) % mo; } } } return C; } void solve() { for(int i = 1;i <= k;i++) { for(int j = 1;j <= k;j++) { if(j == i + 1)op.v[i][j] = 1; if(i == k)op.v[i][j] = 1; } } op.x = k;op.y = k; f.v[k][1] = 1; f.x = k;f.y = 1; while(n) { if(n & 1)f = mtMul(op,f); n >>= 1; op = mtMul(op,op); } printf("%lld\n",f.v[k][1]); } int main() { init(); readdata(); solve(); return 0; }