知识点简单总结——常系数齐次线性递推

知识点简单总结——常系数齐次线性递推

Cayley-Hamilton定理

参考自:https://www.cnblogs.com/regenwald/articles/7804744.html

令 $ A $ 为一个 $ n \times n $ 阶的矩阵, $ p( \lambda ) $ 为其特征方程,即 $ \det{ ( A - \lambda I ) } $ 。

则有 $ p( A ) = 0 $ 。证明参见上述博客。

之后证明会用到这个定理。

常系数齐次线性递推

给定 $ f_{0...k-1} , c_{1...k} $ ,求 $ f_{n} = \sum\limits_{ i = 1 }^{ k } f_{n-i} c_{i} $ , $ n \le 10^{9} , k \le 32000 $ 。

很明显有 $ O(nk) $ 的暴力和 $ O( k^{ 3 } \log { n } ) $ 的矩阵乘法。

考虑下矩阵乘法的实现过程,

设转移矩阵为 $ A $ ,初始向量为 $ S $ 。

毫无疑问对于只需要求一个 $ f_{n} $ 的情况下矩阵里的东西太多了,考虑省掉一部分。

考虑构造序列 $ g $ 有 $ A^{ n } = \sum\limits_{ i = 0 }^{ k - 1 } g_{ i } A^{ i } $ 。

那么由于仅仅需要求出数列一项的答案,我们直接把上式改成求

\[S \times A^{ n } = S \times \sum\limits_{ i = 0 }^{ k - 1 } g_{ i } A^{ i } \]

进而答案就是

\[ans = ( S \times A^{ n } )_{ 0 } = \sum\limits_{ i = 0 }^{ k - 1 } g_{ i } ( S \times A^{ i } )_{ 0 } \]

...不对啊,这 $ ( S \times A^{ i } )_{ 0 } $ ...

不就是 $ S_{i} $ 。

好了答案出来了: $ ans = \sum\limits_{ i = 0 }^{ k - 1 } g_{ i } S_{ i } $ 。

准备考虑求 $ g $ 数组。

转移是一个矩阵乘一个向量得到一个新向量,考虑写出特征方程然后变为特征多项式:

\[x^{k} = \sum\limits_{ i = 1 }^{ k } { c_{ i } x_{ k - i } } \\ H( x ) = x^{k} - \sum\limits_{ i = 1 }^{ k } { c_{ i } x_{ k - i } } \]

由Cayley-Hamilton定理有 $ H(A) = 0 $ , $ 0 $ 指 $ 0 $ 矩阵。

集合上述结论考虑将求 $ A^{N} \ mod \ H(A) = G(A) $ 。

很巧的求出结果就是要求的 $ g $ 数组。

上式直接当 $ x^{N} $ 的多项式取模就好。

(是不是有点细节没说清诶。。。算了咕了吧反正主体思路到位了(被打))

最终时间复杂度为 $ O( k \cdot \log{ k } \cdot \log{ m } ) $ 。

洛谷板子题

多项式取模注意预处理下模数数组的的逆和其反转数组的逆,不然会T死(

代码常数略大,开 $ O2 $ 在 $ 1s $ 左右。

#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
template<typename TP>inline void read(TP &tar)
{
	TP ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
	tar=ret*f;
}
template<typename TP,typename... Args>inline void read(TP& t,Args&... args){read(t),read(args...);}
namespace RKK
{
const int N=140011,maxn=131072;
const int mo=998244353,G=3;
void doadd(int &a,int b){if((a+=b)>=mo) a-=mo;}int add(int a,int b){return (a+=b)>=mo?a-mo:a;}
void domul(int &a,int b){a=1ll*a*b%mo;}int mul(int a,int b){return 1ll*a*b%mo;}
int fpow(int a,int p){int ret=1;while(p){if(p&1) domul(ret,a);domul(a,a),p>>=1;}return ret;}
int wg[N],iwg[N];int rev[N],lastlen;
inline void init(){for(int i=1;i<maxn;i<<=1) wg[i]=fpow(G,(mo-1)/(i<<1)),iwg[i]=fpow(wg[i],mo-2);}
inline void getrev(int len){for(int i=1;i<len;i++)rev[i]=(rev[i>>1]>>1)|((i&1)*(len>>1));}
inline void ntt(int *a,int len,int tp)
{
	if(lastlen!=len) getrev(len),lastlen=len;
	for(int i=0;i<len;i++)if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=1;i<len;i<<=1)
	{
		int w0=(~tp)?wg[i]:iwg[i];
		for(int j=0;j<len;j+=(i<<1))
		{
			int w=1;
			for(int k=0;k<i;k++,domul(w,w0))
			{
				int w1=a[j+k],w2=mul(w,a[j+k+i]);
				a[j+k]=add(w1,w2),a[j+k+i]=add(w1,mo-w2);
			}
		}
	}
	int ilen=fpow(len,mo-2);
	if(tp==-1)for(int i=0;i<len;i++) domul(a[i],ilen);
}
namespace poly
{
void clear(int *a,int n){memset(a,0,n*4);}
void copy(int *a,int *b,int n){memcpy(a,b,n*4);}
namespace inv
{
	int a[N],b[N];
	void work(int *f,int *g,int n)
	{
		g[0]=fpow(f[0],mo-2);
		for(int len=1;len<n<<1;len<<=1)
		{
			copy(a,f,len),copy(b,g,len);
			ntt(a,len<<1,1),ntt(b,len<<1,1);
			for(int i=0;i<len<<1;i++) g[i]=mul(b[i],(2ll-mul(a[i],b[i])+mo));
			ntt(g,len<<1,-1),clear(g+len,len);
		}
		clear(a,n*2),clear(b,n*2);
	}
}
}
int t[N];
int n,m,ans,len;
int f[N],g[N],a[N],b[N],c[N],ic[N];
void mod(int *a,int n,int m)
{
	int nn=n-m+1;for(int i=0;i<nn;i++) t[i]=a[n-1-i];
	ntt(t,len,1);for(int i=0;i<len;i++) domul(t[i],ic[i]);ntt(t,len,-1);
	reverse(t,t+nn),poly::clear(t+nn,len-nn);
	ntt(t,len,1);for(int i=0;i<len;i++) domul(t[i],c[i]);ntt(t,len,-1);
	for(int i=0;i<m-1;i++) a[i]=add(a[i],mo-t[i]);poly::clear(a+m-1,len-(m-1));
	poly::clear(t,len);
}
int main()
{
	init();
	read(n,m);for(int i=1;i<=m;i++) read(c[i]),doadd(c[i],mo);
	for(int i=0;i<m;i++) read(f[i]),doadd(f[i],mo);if(n<m) return printf("%d\n",f[n]),0;
	len=1;while(len<=m) len<<=1;
	reverse(c+1,c+1+m);for(int i=0;i<m;i++) c[i]=add(0,mo-c[i+1]);c[m]=1;
	for(int i=0;i<=m;i++) b[i]=c[m-i];poly::inv::work(b,ic,len);
	g[0]=1;a[1]=1;len<<=1;
	ntt(c,len,1),ntt(ic,len,1);
	while(n)
	{
		if(n&1)
		{
			ntt(g,len,1),ntt(a,len,1);for(int i=0;i<len;i++) domul(g[i],a[i]);ntt(g,len,-1),ntt(a,len,-1);
			mod(g,len,m+1);
		}
		ntt(a,len,1);for(int i=0;i<len;i++) domul(a[i],a[i]);ntt(a,len,-1);
		mod(a,len,m+1);
		n>>=1;
	}
	for(int i=0;i<m;i++) doadd(ans,mul(f[i],g[i]));printf("%d\n",ans);
	return 0;
}
}
int main(){return RKK::main();}

应用

不是很明显了嘛。

例题

bzoj4161 Shlw loves matrixI

[NOI2017]泳池

posted @ 2020-07-24 08:12  RikukiIX  阅读(304)  评论(0编辑  收藏  举报