写在前面:
定义矩阵:
struct Matrix{
LL a[MAX_K][MAX_K],n,m;//n行 m列
Matrix(int _n = 0,int _m = 0):n(_n),m(_m){rep(i,1,n)rep(j,1,m) a[i][j]=0;}
void dw(int _n,int _m){n=_n,m=_m;rep(i,1,n)rep(j,1,m) a[i][j]=(i==j?1:0);}
LL& operator ()(int i,int j){return a[i][j];}
void print(){rep(i,1,n) rep(j,1,m) printf("%I64d%c",a[i][j],j==m?'\n':' ');}
};
矩阵乘法:
优化递推主要应用到矩阵乘法(当然如果要说特征根方程求通项的话....),先简单介绍一下矩阵乘法(可以无视掉):
定义矩阵A,B。
当且仅当A .m==B.n时 矩阵A,B可乘。
A(x,y)*B(y,z)=C(x,z)
其中每一个元素 Cij=Aik*Bkj (1<=k<=y)
矩阵乘法满足结合律与分配律,结合律是矩阵能够优化递推的关键所在。
矩阵优化递推的一般方法:
构建一个N维向量A(1,n),一个N*N的转移矩阵B(n,n)
A(1,n)*B(n,n)=C(1,n) 我们发现C与A 同阶,C 又能乘B ...就这样一直递推到需要的一项。
但一般先写出A和下一项C ,再来倒推转移矩阵B
矩阵快速幂:
矩阵乘法一次是O(n^3)的,单凭矩阵乘法优化递推是不可行的。
当然,上面说过矩阵乘法满足结合律,我们考虑矩阵快速幂!
比如要得到递推式的第K项,相当于计算 A*B^K=C
B^K可以通过矩阵快速幂O(N^3logK)算出
Matrix operator *(Matrix a,Matrix b){//矩阵乘法
Matrix res(a.n,b.m);
rep(i,1,a.n) rep(j,1,b.m) rep(k,1,a.m)
(res(i,j)+=a(i,k)*b(k,j))%=MOD;
return res;
}
Matrix operator ^(Matrix a,LL x){//快速幂
Matrix res;res.dw(a.n,a.m);
for (;x;a=a*a,x>>=1)
if (x&1) res=res*a;
return res;
}
矩阵的重要性质:
贴吧大神说矩阵也满足费马小定理?(亟待验证)
http://tieba.baidu.com/p/2472273208
几个矩阵优化的大类:
1.求数列第K项
求fibonacci数列(1,1,2,3,5...)第K项
这个应该是矩阵优化最基础的了,大家都会。
令Fi表示fibonacci数列第i项
构造向量 A=(Fi,Fi-1),他的下一项是C=(Fi+1,Fi)
倒推转移矩阵B A*B=C
初始是(F1,F0).(F2,F1)..都无所谓啦,。
然后求fibonacci数列第k项就是 A*B^K=C 中的值,根据初始矩阵选值调整一下+-1什么的...
这个例子能发现些什么呢,呃。。没什么。。
[Noi2012]随机数生成器
http://www.lydsy.com/JudgeOnline/problem.php?id=2875
简要描述:
给定数列递推式 求数列{X}的第n项 mod g。
数据范围:
1: n<=100, m,a,c,X0<=100 m是质数
2: n<=1000, m,a,c,X0<=1000 m是质数
3: n<=10^4, m,a,c,X0<=10^4 m是质数
4: n<=10^4, m,a,c,X0<=10^4 m是质数
5: n<=10^5, m,a,c,X0<=10^4 m与a-1互质
6: n<=10^5, m,a,c,X0<=10^4 m与a-1互质
7: n<=10^5, m,a,c,X0<=10^4 m与a-1互质
8: n<=10^6, m,a,c,X0<=10^4
9: n<=10^6, m,a,c,X0<=10^9 m是质数
10:n<=10^6, m,a,c,X0<=10^9
11:n<=10^12, m,a,c,X0<=10^9 m是质数
12:n<=10^12, m,a,c,X0<=10^9 m是质数
13:n<=10^16, m,a,c,X0<=10^9 m与a-1互质
14:n<=10^16, m,a,c,X0<=10^9 m与a-1互质
15:n<=10^16, m,a,c,X0<=10^9
16:n<=10^18, m,a,c,X0<=10^9
17:n<=10^18, m,a,c,X0<=10^9
18:n<=10^18, m,a,c,X0<=10^18 m是质数
19:n<=10^18, m,a,c,X0<=10^18 m与a-1互质
20:n<=10^18, m,a,c,X0<=10^18
这是一个一阶递推,可以尝试多种方法去解决下:
(1)暴力就不说了。。50分。
(2).求通项:
通项一般一阶还能求,上了二阶之后用特征根似乎就会冒出很多无理数系数..(就像上面的fibonacci)
a==1 等差数列....不说了。
a!=1用什么不动点法之类的乱搞一下:
发现里面有个除法,看数据范围,里面有很多m是质数或者m与a-1互质,可以用Euler定理【(a-1)^(phi(m)-1)】或者扩展欧几里德求出逆元化除为乘,
这样加上暴力就有80分。
不错。但是不互质怎么办....
(3)矩阵乘法:
递推式里面有常数有系数 就可以这样搞转移矩阵。
边乘边模就行了,完全不用考虑m是不是质数。
By the way,这题还有个trick,m<=10^18次方,直接乘是要爆long long 的。 写个龟速乘(这名字..),就是化乘为加,这里不介绍了。
这大概不是NOI有史以来最水的题。。
另外也不是所有递推用矩阵都是最好的,例如:
[Noi2013]矩阵游戏
(虽然我还没写过) <- http://blog.csdn.net/sdogsq/article/details/20129081
F[1][1]=1
F[i,j]=a*F[i][j-1]+b (j!=1)
F[i,1]=c*F[i-1][m]+d (i!=1)
递推式中a,b,c,d都是给定的常数。
现在婷婷想知道F[n][m]的值是多少,请你帮助她。由于最终结果可能很大,你只需要输出F[n][m]除以1,000,000,007的余数。
看上去一切都很和谐,但是,真的用矩阵做好做吗??(什么log10快速幂....)
这题的重点是模的是一个质数!!!!
所以我们只用求出来F[n][m]的通项就可以了!
注意到n,m是巨大无比的,但是我们的MOD是质数啊~
费马小定理 a^(p-1)=1 (%p)
所以我们的a^n = a^(n%(p-1)) (% p)
再用之求逆元....
搞了半天一个快速幂就行了。
这又说明了什么呢,如果模的不是质数或者关键的东西不互质,,就是求不出逆元的话。矩阵是首先考虑的。
当然MOD 是一个质数当然也要考虑矩阵,不过也可以考虑看看能不能直接求通项,比如说矩阵想不出来。 看哪种好吧。
灵活使用什么Euler定理,什么数论的...我们这些数学蒟蒻..怎么办啊。。
2.求数列前K项和。
上面的都是求数列第K项是多少,还有的题是求数列前K项的和。Sum=F1+F2+...+Fk
其实这个也很简单啦。
还是拿Fibonacci数列说事吧。
把上面那个矩阵加一维,稍稍改进一下就行了。
这是一个普适性极强的求和法。按理说任何数列都能求和吧。
我目前见到的一般就是上面两类问题(以后见到其他类别还会添加),但矩阵不会考很裸的题,看到N<10^18之类的不是恶心的数位DP就是矩阵优化!
其实矩阵的题关键在于到底该怎么用矩阵优化。
2014年2月28日更新:
如果矩阵也满足费马小定理,那这个矩阵游戏就直接再见了!!!!(有时间写一写)