JZOJ 4276【NOIP2015模擬10.28A組】遞推
阿新 • • 發佈:2020-07-23
【NOIP2015模擬10.28A組】遞推
思路一
對於 \(30%\) 的資料,由於 \(n\) 和 \(x_i\) 都比較小,所以依題暴力列舉每個整點的座標算貢獻即可
思路二
對於額外 \(20%\) 的資料,發現 \(n=1\) 且有數列 \(F\) 為斐波那契數列,於是就變成求 \(\sum_{i=0}^{x_0 - 1}Fib_i\)
於是我們可以矩陣優化求和
思路三
既然提到矩陣,我們不妨順著這個思路來想
如果只有一維,我們很容易用矩陣加速遞推切掉它
那麼考慮高維
發現唯一剩下的問題是如何計算括號中的矩陣之和
因為他們出現了等比
於是考慮暫且拋開單位矩陣 \(I\)
記 F = \(A + A^2 + A^3 + A^4 + ... + A^n\)
\(mid = \lfloor \frac{n}{2} \rfloor\)
\[F(n) = \left \{ \begin{aligned} (A + A^2 + A^3 + A^4 + ... + A^{mid})(A^{mid} + I) & & (\texttt{n is even}) \\ (A + A^2 + A^3 + A^4 + ... + A^{mid})(A^{mid} + I) + A^n & & (\texttt{n is odd}) \end{aligned} \right. \]
由於題目比較噁心,即使 \(A^n\) 用矩陣快速冪算也會 \(T\)
因為快速冪和分治過程性質一樣
所以我們考慮在分治的過程中算出 \(A^n\)
詳見程式碼
\(Code\)
#include<cstdio> #include<cstring> #include<iostream> using namespace std; typedef long long LL; const int N = 15 , M = 45; const int P = 1e9 + 9; int n , m , c[M] , f[M]; LL ans; struct matrix{ int m[M][M]; }A , Q , I , Now , Sum , res; inline matrix Mul(matrix a , matrix b) //矩陣乘法 { memset(res.m , 0 , sizeof res.m); for(register int i = 1; i <= m; i++) for(register int j = 1; j <= m; j++) for(register int k = 1; k <= m; k++) res.m[i][j] = (res.m[i][j] + 1LL * a.m[i][k] * b.m[k][j] % P) % P; return res; } inline matrix Plus(matrix a , matrix b) //矩陣加法 { memset(res.m , 0 , sizeof res.m); for(register int i = 1; i <= m; i++) for(register int j = 1; j <= m; j++) res.m[i][j] = (a.m[i][j] + b.m[i][j]) % P; return res; } inline matrix divide(int x) { if (x == 1) return Q = A; int mid = x >> 1; matrix tmp = divide(mid); //分而治之 matrix temp; temp = Mul(tmp , Q); tmp = Plus(tmp , temp); Q = Mul(Q , Q); //平方算A^{2*mid} if (!(x & 1)) return tmp; Q = Mul(Q , A); //奇數時再乘個A,和快速冪同理 tmp = Plus(tmp , Q); return tmp; } int main() { freopen("recursion.in" , "r" , stdin); freopen("recursion.out" , "w" , stdout); scanf("%d%d" , &n , &m); for(register int i = 1; i <= m; i++) scanf("%d" , &c[i]); for(register int i = 0; i < m; i++) scanf("%d" , &f[i]); for(register int i = 1; i <= m; i++) A.m[i + 1][i] = 1 , I.m[i][i] = Sum.m[i][i] = 1; for(register int i = 1; i <= m; i++) A.m[i][m] = c[m - i + 1]; int x; for(register int i = 1; i <= n; i++) { scanf("%d" , &x); Q = I; Now = divide(x - 1); for(register int j = 1; j <= m; j++) Now.m[j][j] = (Now.m[j][j] + 1) % P; //加上單位矩陣I Sum = Mul(Now , Sum); //先算括號中的 } for(register int i = 1; i <= m; i++) //把f乘上來 ans = (ans + 1LL * f[i - 1] * Sum.m[i][1] % P) % P; printf("%lld" , ans); }