【学习笔记】特征多项式

前置芝士

矩阵

矩阵乘法的定义应当是众所周知的,称 C = A × B C=A\times B C=A×B 当且仅当 C i k = ∑ j A i j B j k C_{ik}=\sum_{j}A_{ij}B_{jk} Cik=jAijBjk

加法就是对应的位置相加。具有加法交换律,加法、乘法结合律,乘法对加法的分配律。

向量

n n n 维向量点乘 ( a 0 , a 1 , a 2 , … , a n − 1 ) ⋅ ( b 0 , b 1 , b 2 , … , b n − 1 ) = ∑ i = 0 n − 1 a i b i (a_0,a_1,a_2,\dots,a_{n-1})\cdot (b_0,b_1,b_2,\dots,b_{n-1})=\sum_{i=0}^{n-1}a_i b_i (a0,a1,a2,,an1)(b0,b1,b2,,bn1)=i=0n1aibi

加法也是对应位置相加。具有加法、乘法交换律,加法结合律,点乘对加法的分配率。

为了叙述方便,行向量与列向量相乘均代表向量的点乘。还有,零向量(以及全零的矩阵)记作 o ⇀ \overrightharpoon{\bf{o}} o

齐次常系数递推

形如 x n = ∑ i = 1 k a i x n − i x_{n}=\sum_{i=1}^{k}a_{i}x_{n-i} xn=i=1kaixni

  • 齐次:总是由前 k k k 个数得到第 k + 1 k+1 k+1 个数。
  • 常系数:系数 a i a_i ai 不随 n n n 的改变而改变。
  • 递推:这都不知道,就可以不用看了。

特征向量

定义

对于一个 k × k k\times k k×k 的矩阵 A A A ,可能存在 k × 1 k\times 1 k×1 的非零列向量 v ⇀ \overrightharpoon{\bf{v}} v ,与一个常数 λ \lambda λ ,满足

A v ⇀ = λ v ⇀ A\overrightharpoon{\bf{v}}=\lambda\overrightharpoon{\bf{v}} Av =λv

然后我们把 v ⇀ \overrightharpoon{\bf{v}} v 叫做特征向量。但是为什么呢?

求解

∑ i = 1 n a x i v i = λ v x \sum_{i=1}^{n}a_{xi}v_i=\lambda v_x i=1naxivi=λvx

解方程即可。最后得到的方程式为 λ k = ∑ i = 1 k a i λ k − i \lambda^k=\sum_{i=1}^{k}a_i\lambda^{k-i} λk=i=1kaiλki

毫无疑问,它有 k k k 个根,但是可能相同。这里的 λ \lambda λ 已经扩大到复数域。

性质

v ⇀ \overrightharpoon{\bf{v}} v 有无数多个。显然,如果 v ⇀ = v 0 ⇀ \overrightharpoon{\bf{v}}=\overrightharpoon{\bf v\rm{_0}} v =v0 是一个解,那么 v ⇀ = γ v 0 ⇀ ( γ ∈ R ) \overrightharpoon{\bf v}=\gamma\overrightharpoon{\bf v\rm _0}(\gamma\in\R) v =γv0 (γR) 也是一个解。

无论怎么说,对于某一个 λ \lambda λ ,所有的 v ⇀ \overrightharpoon{\bf v} v n n n 维空间中是共线的。

特征多项式

众所周知,齐次常系数递推式可以使用矩阵乘法快速幂进行优化,复杂度 O ( k 3 log ⁡ n ) \mathcal O(k^3\log n) O(k3logn)

不妨假设转移矩阵为 A A A ,递推方程为标准形式,状态向量 v i ⇀ = ( x i , x i − 1 , x i − 2 , … , x i − k + 1 ) \overrightharpoon{\bf{v}\mathnormal{_i}}=(x_i,x_{i-1},x_{i-2},\dots,x_{i-k+1}) vi =(xi,xi1,xi2,,xik+1)

我们现在的目标就是找到 λ \lambda λ v ⇀ \overrightharpoon{\bf{v}} v (也就是特征向量),满足 A v ⇀ = λ v ⇀ A\overrightharpoon{\bf{v}}=\lambda\overrightharpoon{\bf{v}} Av =λv

那么我们求出 k k k λ \lambda λ ,记为 λ 0 , λ 1 , λ 2 , … , λ k − 1 \lambda_0,\lambda_1,\lambda_2,\dots,\lambda_{k-1} λ0,λ1,λ2,,λk1

我们引入一个新的多项式 f ( x ) = ∏ i = 0 k − 1 ( x − λ i ) f(x)=\prod_{i=0}^{k-1}\left(x-\lambda_i\right) f(x)=i=0k1(xλi)

这个 f ( x ) f(x) f(x) 是无需计算的——它恰好就是特征方程移项后的结果 λ k − ∑ i = 0 k − 1 a k − i λ i \lambda^k-\sum_{i=0}^{k-1}a_{k-i}\lambda^i λki=0k1akiλi

根据多项式取模,可以得到 g ( x ) , r ( x ) g(x),r(x) g(x),r(x),满足 x n − k = g ( x ) f ( x ) + r ( x ) (VIP) x^{n-k}=g(x)f(x)+r(x)\tag{VIP} xnk=g(x)f(x)+r(x)(VIP)

此处可以使用快速幂,就是一个带取模的快速幂,计算 r ( x ) ≡ x n − k ( m o d f ( x ) ) r(x)\equiv x^{n-k}\pmod{f(x)} r(x)xnk(modf(x))

复杂度是 O ( k log ⁡ k log ⁡ n ) \mathcal O(k\log k\log n) O(klogklogn)的,还不算太差。

然后我们试着把 f ( x ) f(x) f(x)的定义域转移到矩阵上。其实很简单,因为矩阵的幂与实数的幂是相似的,可以直接写出 f ( A ) = ∏ i = 0 k − 1 ( A − λ i I ) f(A)=\prod_{i=0}^{k-1}(A-\lambda_iI) f(A)=i=0k1(AλiI)

其中 I I I是单位矩阵, A A A是一个矩阵。乘法就是矩阵乘法。

如果我们的 A A A取题目中的转移矩阵,那么我们一定有这个结论: f ( A ) = o ⇀ f(A)=\overrightharpoon{\bf o} f(A)=o

注:我现在证明不了……

所以将其带入 ( VIP ) (\text{VIP}) (VIP)式中,得到 A n − k = r ( A ) A^{n-k}=r(A) Ank=r(A)

不妨设 r ( x ) r(x) r(x)的系数为 { b i } ( 0 ≤ i < k ) \{b_i\}(0\le i{bi}(0i<k),我们想要计算的是 A n − k v k ⇀ A^{n-k}\overrightharpoon{\bf v\mathnormal{_k}} Ankvk ,即 r ( A ) v k ⇀ = ∑ i = 0 k − 1 b i A i v k ⇀ r(A)\overrightharpoon{\bf v\mathnormal{_k}}=\sum_{i=0}^{k-1}b_iA^i\overrightharpoon{\bf v\mathnormal{_k}} r(A)vk =i=0k1biAivk

注意到 A i v k ⇀ = v k + i ⇀ = ( x k + i , x k + i − 1 , x k + i − 2 , … , x i + 1 ) A^i\overrightharpoon{\bf v\mathnormal{_k}}=\overrightharpoon{\bf v\mathnormal{_{k+i}}}=(x_{k+i},x_{k+i-1},x_{k+i-2},\dots,x_{i+1}) Aivk =vk+i =(xk+i,xk+i1,xk+i2,,xi+1),所以我们实际上要求的就是 r ( A ) v k ⇀ = ∑ i = 0 k − 1 b i v k + i ⇀ r(A)\overrightharpoon{\bf v\mathnormal{_k}}=\sum_{i=0}^{k-1}b_i\overrightharpoon{\bf v\mathnormal{_{k+i}}} r(A)vk =i=0k1bivk+i

所以我们只需要暴力计算出 x k + 1 , x k + 2 , x k + 3 , … , x 2 k x_{k+1},x_{k+2},x_{k+3},\dots,x_{2k} xk+1,xk+2,xk+3,,x2k,然后直接计算即可。

总时间复杂度是 O ( k 2 + k log ⁡ k log ⁡ n ) \mathcal O(k^2+k\log k\log n) O(k2+klogklogn)的。当然,你暴力进行多项式乘法(与取模)也不会太慢的。

例题

模板

传送门 to VJ

#include 
#include 
#include 
using namespace std;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 2005, Mod = 1e9+7;

typedef vector<int> poly;
poly operator * (const poly &a,const poly &b){
	poly c; c.resize(a.size()+b.size()-1);
	for(int i=0,lena=a.size(); i<lena; ++i)
		for(int j=0,lenb=b.size(); j<lenb; ++j)
			c[i+j] = (c[i+j]+1ll*a[i]*b[j])%Mod;
	return c;
}
poly &operator %= (poly &a,const poly &b){
	for(int i=int(a.size())-1,lenb=b.size(); i>=lenb-1; --i){
		int f = a[i]/b[lenb-1];
		for(int j=lenb; j; --j){
			a[i+j-lenb] -= 1ll*b[j-1]*f%Mod;
			a[i+j-lenb] = (a[i+j-lenb]+Mod)%Mod;
		}
	}
	while(not a.empty() and a.back() == 0)
		a.pop_back(); // 末尾的零
	return a;
}

int h[MaxN<<1], a[MaxN], n, k;
void input(){
	n = readint(), k = readint();
	for(int i=1; i<=k; ++i)
		a[i] = (readint()+Mod)%Mod;
	for(int i=0; i<k; ++i)
		h[i] = (readint()+Mod)%Mod;
}
inline poly qkpow(poly f,int q,poly Mod){
	poly ans; ans.push_back(1); // Ans(x) = 1
	for(; q; q>>=1,f=f*f,f%=Mod)
		if(q&1) ans = ans*f, ans %= Mod;
	return ans;
}
void solve(){
	for(int i=k; i<=(k<<1); ++i)
		for(int j=i-k; j<i; ++j)
			h[i] = (h[i]+1ll*h[j]*a[i-j])%Mod;
	if(n <= (k<<1)){
		printf("%d\n",h[n]);
		return ;
	}

	poly f; f.resize(k+1); // f(x)
	for(int i=0; i<k; ++i)
		f[i] = (Mod-a[k-i])%Mod;
	f[k] = 1; // f(x) = x^k - ...
	poly only_x; only_x.resize(2);
	only_x[0] = 0, only_x[1] = 1;
	f = qkpow(only_x,n-k,f);

	int ans = 0;
	for(int i=0; i<k; ++i)
		ans = (ans+1ll*f[i]*h[i+k])%Mod;
	printf("%d\n",ans);
}

int main(){
	input(), solve();
	return 0;
}

你可能感兴趣的:(C++,数学,#,多项式)