f k = ∑ i = 1 n a i f k − i f_k=\sum _{i=1}^{n} a_if_{k-i} fk=i=1∑naifk−i
形如上式的 d p dp dp转移式( f f f表示 d p dp dp状态, a a a表示转移系数)即为常系数齐次线性递推式。对于这样的 d p dp dp式,给定 f 1 , 2 , . . , n , a 1 , 2 , . . . , n f_{1,2,..,n},a_{1,2,...,n} f1,2,..,n,a1,2,...,n,即可递推出 f k ( k > n ) f_k(k>n) fk(k>n)。
常系数:每一项均由前 n n n项乘以固定系数得到。齐次:都为一次式。线性递推:显然。
矩阵快速幂优化上式达到 O ( n 3 log k ) O(n^3\log k) O(n3logk)的复杂度(但这不是本文的重点)。
而常系数齐次线性递推式的转移矩阵的特征多项式可以 O ( 读 入 ) O(读入) O(读入)求出,所以通过特征多项式可以进一步优化到 O ( n 2 log k ) O(n^2 \log k) O(n2logk),再进一步优化多项式取模后可以做到 O ( n log n log k ) O(n\log n\log k ) O(nlognlogk)(本文讲解 O ( n 2 log k ) O(n^2\log k) O(n2logk)的暴力取模方法)。
设 A A A为 n n n阶矩阵,如果存在常数 λ \lambda λ及非零向量 v ⃗ \vec v v,使得 A v ⃗ = λ v ⃗ A\vec v=\lambda\vec v Av=λv,则称 λ \lambda λ为 A A A的特征值(也称缩放系数),非零向量 v ⃗ \vec v v为** A A A的对应于特征值 λ \lambda λ的特征向量**。
则:
A v ⃗ = λ v ⃗ ⟺ ( λ I − A ) v ⃗ = 0 A\vec v=\lambda \vec v \Longleftrightarrow (\lambda I-A)\vec v=0 Av=λv⟺(λI−A)v=0
其有非零解的充要条件: d e t ( λ I − A ) = ∣ λ I − A ∣ = 0 det(\lambda I -A)=|\lambda I-A|=0 det(λI−A)=∣λI−A∣=0
( d e t ( A ) , ∣ A ∣ det(A),|A| det(A),∣A∣是矩阵 A A A的行列式的两种等价的表示方式)
(并不会证明,似乎 d e t ( ) = 0 det()=0 det()=0的一个矩阵可以想象成拍扁成了向量?而一个可逆的( d e t ( ) ≠ 0 det()\neq 0 det()̸=0的矩阵乘上一个向量并不能得到0)。
方程 ∣ λ I − A ∣ = 0 |\lambda I-A|=0 ∣λI−A∣=0称为 A A A的特征方程。
设 A = a ( i j ) A=a(ij) A=a(ij)的秩(列秩/行秩)为 n n n(令 A A A是满秩的),则 A A A的有 n n n组线性不相关特征值和特征向量(唔,证明没有找到)。
则有:
( 1 ) λ 1 + λ 2 + . . . + λ n = a 11 + a 22 + . . . + a n n (1)\lambda_1 +\lambda_2+...+\lambda_n=a_{11}+a_{22}+...+a_{nn} (1)λ1+λ2+...+λn=a11+a22+...+ann
( 2 ) λ 1 λ 2 . . . λ n = ∣ A ∣ (2)\lambda_1\lambda_2...\lambda_n=|A| (2)λ1λ2...λn=∣A∣
结合 ∣ λ I − A ∣ = 0 |\lambda I-A|=0 ∣λI−A∣=0可以简单证明(理解)。
∣ λ I − A ∣ = λ n + k 1 λ n − 1 + . . . + k n − 1 λ + k n |\lambda I-A|=\lambda^n+k_1\lambda^{n-1}+...+k_{n-1}\lambda +k^n ∣λI−A∣=λn+k1λn−1+...+kn−1λ+kn这个 λ \lambda λ的 n n n次多项式称为 A A A的特征多项式 ϕ ( λ ) \phi(\lambda) ϕ(λ)。
也即 ϕ ( x ) = ∣ x I − A ∣ \phi(x)=|xI-A| ϕ(x)=∣xI−A∣。
ϕ ( λ ) = 0 \phi(\lambda)=0 ϕ(λ)=0的 n n n个解即为 A A A的 n n n个特征值。
λ I − A = [ λ − a 11 − a 12 ⋯ − a 1 n − a 21 λ − a 22 ⋯ − a 2 n ⋯ ⋯ ⋯ ⋯ − a n 1 − a n 2 ⋯ λ − a n n ] \lambda I-A=\left [ \begin {matrix} \lambda -a_{11} & -a_{12} & \cdots & -a_{1n} \\ -a_{21} & \lambda -a_{22} &\cdots & -a_{2n} \\ \cdots & \cdots & \cdots & \cdots &\\ -a_{n1} & -a_{n2} &\cdots & \lambda -a_{nn}\\ \end {matrix} \right ] λI−A=⎣⎢⎢⎡λ−a11−a21⋯−an1−a12λ−a22⋯−an2⋯⋯⋯⋯−a1n−a2n⋯λ−ann⎦⎥⎥⎤
该矩阵称为 A A A的特征矩阵。
回归到常系数齐次线性递推式 f k = ∑ i = 1 n a i f k − i f_k=\sum _{i=1}^{n} a_if_{k-i} fk=∑i=1naifk−i:
[ a 1 a 2 a 3 ⋯ a n − 1 a n 1 0 0 ⋯ 0 0 0 1 0 ⋯ 0 0 0 0 1 ⋯ 0 0 ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ 0 0 0 ⋯ 0 0 0 0 0 ⋯ 1 0 ] [ f k − 1 f k − 2 f k − 3 f k − 4 ⋮ f k − ( n − 1 ) f k − n ] = [ f k f k − 1 f k − 2 f k − 3 ⋮ f k − ( n − 2 ) f k − ( n − 1 ) ] \left [ \begin {matrix} a_1 &a_2 & a_3& \cdots &a_{n-1} &a_n\\ 1 & 0&0& \cdots & 0 & 0\\ 0& 1 & 0 &\cdots &0&0\\ 0 & 0&1& \cdots &0&0\\ \cdots &\cdots &\cdots &\cdots &\cdots &\cdots &\\ 0&0&0&\cdots &0 &0\\ 0&0&0&\cdots &1&0 \\ \end {matrix} \right] \left [ \begin{matrix} f_{k-1}\\f_{k-2}\\ f_{k-3}\\f_{k-4}\\ \vdots\\f_{k-(n-1)} \\f_{k-n} \end{matrix} \right ] = \left [ \begin{matrix} f_{k}\\f_{k-1}\\ f_{k-2}\\f_{k-3}\\ \vdots\\f_{k-(n-2)} \\f_{k-(n-1)} \end{matrix} \right ] ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡a1100⋯00a2010⋯00a3001⋯00⋯⋯⋯⋯⋯⋯⋯an−1000⋯01an000⋯00⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡fk−1fk−2fk−3fk−4⋮fk−(n−1)fk−n⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡fkfk−1fk−2fk−3⋮fk−(n−2)fk−(n−1)⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
构造转移矩阵的特征矩阵:
ϕ ( λ ) = ∣ λ I − A ∣ = [ λ − a 1 − a 2 − a 3 ⋯ − a n − 1 − a n − 1 λ 0 ⋯ 0 0 0 − 1 λ ⋯ 0 0 0 0 − 1 ⋯ 0 0 ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ 0 0 0 ⋯ λ 0 0 0 0 ⋯ − 1 λ ] \phi(\lambda)=|\lambda I-A|= \left [ \begin {matrix} \lambda -a_1 & -a_2 & -a_3& \cdots &-a_{n-1} &-a_n\\ -1 & \lambda&0& \cdots & 0 & 0\\ 0& -1 & \lambda &\cdots &0&0\\ 0 & 0&-1& \cdots &0&0\\ \cdots &\cdots &\cdots &\cdots &\cdots &\cdots &\\ 0&0&0&\cdots &\lambda &0\\ 0&0&0&\cdots &-1&\lambda \\ \end {matrix} \right] ϕ(λ)=∣λI−A∣=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡λ−a1−100⋯00−a2λ−10⋯00−a30λ−1⋯00⋯⋯⋯⋯⋯⋯⋯−an−1000⋯λ−1−an000⋯0λ⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤
这里可以强行手算一下行列式(全排列逆序对方法),因为这个矩阵每列最多只有三行不为0,所以可以模拟/枚举一下:
综上:
(1) ϕ ( λ ) = ∣ λ I − A ∣ = λ n − ∑ i = 1 n a i λ n − i \phi(\lambda)=|\lambda I-A|=\lambda ^{n}-\sum_{i=1}^n a_i\lambda ^{n-i}\tag 1 ϕ(λ)=∣λI−A∣=λn−i=1∑naiλn−i(1)
这样就得到了特征多项式的每项系数。
由 ϕ ( A ) = ∣ A I − A ∣ = 0 \phi(A)=|AI-A|=0 ϕ(A)=∣AI−A∣=0得到 ϕ ( A ) \phi(A) ϕ(A)为0矩阵。
但似乎上面这个证明是错的?
前文已经说明 ϕ ( x ) \phi(x) ϕ(x)是一个 n n n次多项式,同样由 ϕ ( λ ) = 0 \phi(\lambda)=0 ϕ(λ)=0可以用插值法表达为: ϕ ( x ) = ∏ i = 1 n ( λ i − x ) \phi(x)=\prod \limits_{i=1}^n (\lambda_i -x) ϕ(x)=i=1∏n(λi−x)。
则 ϕ ( A ) = ∏ i = 1 n ( λ i I − A ) \phi(A)=\prod \limits_{i=1}^n (\lambda_iI-A) ϕ(A)=i=1∏n(λiI−A)。显然对于所有 1 ≤ i ≤ n 1\leq i\leq n 1≤i≤n都有 ϕ ( A ) v ⃗ i = 0 \phi(A)\vec v_i=0 ϕ(A)vi=0,又因为 v ⃗ i \vec v_i vi互相线性不相关,所以 A A A变换得到的 ϕ ( A ) \phi(A) ϕ(A)矩阵为0矩阵。得证。
假设原本需要矩阵快速幂求得 A k A^{k} Ak,现在去掉无用的 0 0 0矩阵,答案是相同的,实际上就是多项式取模的过程,最终得到一个最高次幂为 n − 1 n-1 n−1次的多项式 g ( A ) g(A) g(A):
(2) g ( A ) = A k % ϕ ( A ) = ∑ i = 0 n − 1 p i A i g(A)=A^k\%\phi(A)=\sum _{i=0}^{n-1} p_i A^i\tag 2 g(A)=Ak%ϕ(A)=i=0∑n−1piAi(2)
设初始递推矩阵为: f [ 1 ] = f n , f n − 1 , . . . , f 1 f[1]=f_n,f_{n-1},...,f_1 f[1]=fn,fn−1,...,f1,则最终得到: f [ 1 ] A k = f [ 1 ] g ( A ) = f [ 1 + k ] f[1]A^k=f[1]g(A)=f[1+k] f[1]Ak=f[1]g(A)=f[1+k],进一步转化为 f [ 1 + k ] = ∑ i = 0 n − 1 p i f [ 1 + i ] f[1+k]=\sum\limits_{i=0}^{n-1}p_if[1+i] f[1+k]=i=0∑n−1pif[1+i],但实际上我们只需要 f [ 1 + k ] f[1+k] f[1+k]中的最后一项 f 1 + k f_{1+k} f1+k,可以将矩阵加法展开,故:
(3) f 1 + k = ∑ i = 0 n − 1 p i f 1 + i f_{1+k}=\sum\limits_{i=0}^{n-1}p_if_{1+i}\tag 3 f1+k=i=0∑n−1pif1+i(3)
常系数齐次线性递推优化的过程即为:
( 1 ) (1) (1)求出转移矩阵 A A A的特征多项式 ϕ ( A ) \phi(A) ϕ(A)
( 2 ) (2) (2)以 ϕ ( A ) \phi(A) ϕ(A)为模做 A A A的快速幂得到 A k A^k Ak
( 3 ) (3) (3)解出 n − 1 n-1 n−1次多项式 g ( A ) g(A) g(A)的每项系数转移得到答案
这里补充一点知识:
关于多项式取模法则:
如: 5 x 5 + 3 x 4 + x 3 + x 2 + 6 x + 1 m o d    ( x 3 + x 2 + x + 1 ) 5x^5+3x^4+x^3+x^2+6x+1 \mod (x^3+x^2+x+1) 5x5+3x4+x3+x2+6x+1mod(x3+x2+x+1)
我们先把 ( x 3 + x 2 + x + 1 ) (x^3+x^2+x+1) (x3+x2+x+1)乘以 x 2 x^2 x2,把被除的多项式中的 5 x 5 5x^5 5x5 消掉(做减法)
然后以此把次高位消掉,直到消到 x 3 x^3 x3为止。
来源
这是一个 O ( n 2 ) O(n^2) O(n2)的暴力取模。
O ( n log n ) O(n\log n) O(nlogn)的取模看这里。
bzoj4161
一道真·模板
#include
#define gc getchar()
#define si isdigit(ch)
#define RI register
using namespace std;
const int N=4002,mod=1e9+7;
int ans,n,K,a[N],b[N],c[N],e[N],re[N];
char ch;
inline void rd(int &x)
{
ch=gc;x=0;int f=0;
for(;!si;ch=gc) if(ch=='-') f=1;
for(;si;ch=gc) x=x*10+(ch^48);
if(f) x=mod-x;
}
inline int ad(int x,int y){x+=y;return x>=mod? x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0? x+mod:x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void Mul(int *f,int *g,int n)
{
RI int i,j;
memset(re,0,sizeof(int)*(n+1));
for(i=0;i<=K;++i)
for(j=0;j<=K;++j)
re[i+j]=ad(re[i+j],mul(f[i],g[j]));
for(i=n;i>=K;--i){
if(!re[i]) continue;
for(j=0;j>=1,Mul(c,c,M))
if(n&1) Mul(c,e,M);
for(i=0;i
bzoj4944 noi2017泳池
#include
#define RI register
using namespace std;
const int mod=998244353;
const int N=2005;
int a[N],e[N],c[N],b[N],f[2][N],ss[N];
int pw[N],rvq,q,n,lim,xx,yy,ans;
inline int ad(int x,int y){x+=y;return x>=mod? x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0? x+mod:x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int fp(int x,int y)
{
int re=1;
for(;y;y>>=1,x=mul(x,x))
if(y&1) re=mul(re,x);
return re;
}
inline void Mul(int *f,int *g,int n,int K)
{
RI int i,j,k,t;
memset(ss,0,sizeof(int)*(n+1));
for(i=0;i=K;--i){
if(!ss[i]) continue;
for(j=1;j<=K;++j)
ss[i-j]=ad(ss[i-j],mul(ss[i],b[j]));
ss[i]=0;
}
for(i=0;i<=K;++i) g[i]=ss[i];
}
inline int cal(int n,int lim)
{
RI int i,j,k,t,ori;
memset(f[1],0,sizeof(f[1]));
int *F=f[0],*G=f[1];G[0]=1;
for(i=lim-1;i;--i,swap(F,G)){
memset(F,0,sizeof(int)*(lim+1));
for(j=0;i*j>=1,Mul(c,c,t,ori))
if(n&1) Mul(c,e,t,ori);
for(ans=0,i=0;i