标签:
对于一个线性递推式,求它第项的值,通常的做法是先构造一个的矩阵,然后在时间内求出。
其实,由于这个矩阵的特殊性,可以将时间优化到。接下来我会以一个题目来讲解矩阵乘法递推的优化。
题目:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1229
题意:设,求的值。其中,和
。
前言:本题如果用普通的矩阵做法,很明显会TLE。那么我们要对这个特殊的矩阵进行时间上的优化。
分析:本题主要可用两种方法解决,分别是错位相减和矩阵乘法。先来说说错位相减的基本做法,把题目描述改一下
以来表示,那么有
进而得到
接下来,我们重点关注,因为
对于组合系数相同的进行合并得到
那么可以看出
这是一个递归式,递归出口是当时
对于上述递归式,为了提高效率,需要进行记忆化,当然这里是针对,当时需要特判。
此时的问题就是典型的自然数幂和问题,关于自然数幂和问题的详细讲解,链接如下
自然数幂和:http://blog.csdn.net/acdreamers/article/details/38929067
代码:
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; typedef long long LL; const int N = 2005; const LL MOD = 1000000007; LL n, r; LL C[N][N]; LL B[N],Inv[N]; LL Tmp[N]; LL ans[N]; void Init() { //预处理组合数 for(int i=0; i<N; i++) { C[i][0] = C[i][i] = 1; if(i == 0) continue; for(int j=1; j<i; j++) C[i][j] = (C[i-1][j] % MOD + C[i-1][j-1] % MOD) % MOD; } //预处理逆元 Inv[1] = 1; for(int i=2; i<N; i++) Inv[i] = (MOD - MOD / i) * Inv[MOD % i] % MOD; //预处理伯努利数 B[0] = 1; for(int i=1; i<N; i++) { LL ans = 0; if(i == N - 1) break; for(int j=0; j<i; j++) { ans += C[i+1][j] * B[j]; ans %= MOD; } ans *= -Inv[i+1]; ans = (ans % MOD + MOD) % MOD; B[i] = ans; } } LL quick_mod(LL a, LL b, LL m) { LL ans = 1; a %= m; while(b) { if(b & 1) { ans = ans * a % m; b--; } b >>= 1; a = a * a % m; } return ans; } LL Work1(int k) { LL ans = Inv[k+1]; LL sum = 0; for(int i=1; i<=k+1; i++) { sum += C[k+1][i] * Tmp[i] % MOD * B[k+1-i] % MOD; sum %= MOD; } ans *= sum; ans %= MOD; return ans; } LL Work2(int k) { if(ans[k] != -1) return ans[k]; if(k == 0) { ans[k] = r * (quick_mod(r, n, MOD) - 1) % MOD * quick_mod(r-1, MOD-2, MOD) % MOD; ans[k] = (ans[k] % MOD + MOD) % MOD; return ans[k]; } ans[k] = quick_mod(n+1, k, MOD) * quick_mod(r, n+1, MOD) % MOD * quick_mod(r-1, MOD-2, MOD) % MOD; LL tmp = r * quick_mod(r-1, MOD-2, MOD) % MOD; LL sum = 1; for(int i=k-1; i>=0; i--) { sum += C[k][k-i] * Work2(i); sum %= MOD; } ans[k] -= sum * tmp % MOD; ans[k] = (ans[k] % MOD + MOD) % MOD; return ans[k]; } int main() { int T; Init(); scanf("%d", &T); while(T--) { int k; memset(ans, -1, sizeof(ans)); scanf("%I64d %d %I64d", &n, &k, &r); r %= MOD; if(r == 1) { n %= MOD; Tmp[0] = 1; for(int i=1; i<N; i++) Tmp[i] = Tmp[i-1] * (n + 1) % MOD; LL ret = Work1(k); printf("%I64d\n", ret); continue; } LL ans = Work2(k); printf("%I64d\n", ans); } return 0; }
已经很完美地解决了上述题目,其实还有一个矩阵乘法的做法,这才是我们今天要讨论的重点。以前在HDU上就做过一道与本题差不多的题目,链接是:http://acm.hdu.edu.cn/showproblem.php?pid=3483
几乎跟本题差不多,但是HDU3483的数据比较小,普通的矩阵乘法完全没有压力,但是同样的方法却不能用在此处。
因为本题的比较大,此处行不通。实际上对于递推式构造的矩阵,由于其特殊性,可以对其进行优化,使得时间复
杂度大大降低。接下来,我会用构造矩阵的方法来详细解析本题。设
将按二项式展开得到
那么可以构造如下递推矩阵
接下来,可以通过上面的递推矩阵在时间内求出。但这样做很明显会TLE,由于此矩阵的特殊性
--上三角矩阵,可以将时间优化到。接下来研究如何将一个递推矩阵的时间复杂度优化到。
先来介绍一个很重要的定理----Cayley-Hamilton定理,描述如下
设是阶方阵,是的特征多项式,即,则。
用一句话概括就是:方阵的特征多项式是的化零多项式。
更多关于Cayley-Hamilton定理的学习请戳这里。
本题主要参考这篇文章:《线性递推关系与矩阵乘法》,如下
主要用到本文的如下内容
标签:
原文地址:http://blog.csdn.net/acdreamers/article/details/38929649