求 fn,fn=∑i=1kaifn−if_n,f_n=\sum_{i=1}^ka_if_{n-i}fn,fn=∑i=1kaifn−i
k≤3e4,n≤1e9k\le 3e4,n\le 1e9k≤3e4,n≤1e9
首先暴力的矩阵乘是 k3lognk^3lognk3logn的
引入概念:特征多项式
f(λ)=∣λI−A∣f(\lambda)=|\lambda I-A|f(λ)=∣λI−A∣
III 为单位矩阵
然后对于这道题来讲,转移矩阵是这样的
λI−A=\lambda I-A=λI−A=
λ−a1−a2...−ak−1−ak−1λ0...00−1λ...0...00...−1λ\begin{matrix} \lambda-a_1&-a_2&...&-a_{k-1}&-a_k \\-1&\lambda&0 &...&0 \\0&-1&\lambda&... &0\\& &...&\\0&0&...&-1&\lambda\end{matrix}λ−a1−100−a2λ−10...0λ......−ak−1......−1−ak00λ
对第一行展开然后分别求行列式
f(λ)=(λ−a1)∗A1,1+(−a2)∗A1,2+...+(−ak)∗A1,kf(\lambda)=(\lambda-a_1)*A_{1,1}+(-a_2)*A_{1,2}+...+(-a_k)*A_{1,k}f(λ)=(λ−a1)∗A1,1+(−a2)∗A1,2+...+(−ak)∗A1,k
然后发现去除第一行和某一列后主对角线都是满的,消成下三角矩阵,行列式是主对角线的乘积
f(λ)=λk−∑i=0k−1ak−iλif(\lambda)=\lambda^k-\sum_{i=0}^{k-1}a_{k-i}\lambda^if(λ)=λk−∑i=0k−1ak−iλi
然后由 Cayley−HamiltonCayley-HamiltonCayley−Hamilton 定理
f(A)=0f(A)=0f(A)=0,一个巧妙的伪证是 f(A)=∣AI−A∣=0f(A)=|AI-A|=0f(A)=∣AI−A∣=0
设 R(A)=An%f(A)R(A)=A^n\% f(A)R(A)=An%f(A),即 g(A)∗f(A)+R(A)=Ang(A)*f(A)+R(A)=A^ng(A)∗f(A)+R(A)=An
又因为 f(A)=0f(A)=0f(A)=0 ,所以 An=R(A)A^n=R(A)An=R(A)
看看求的是什么,是向量 vvv,乘上 AnA^nAn 的第 k 列,令f(A)f(A)f(A) 的各项系数为 ccc
即 (v∗An)k=(v∗∑i=0kciAi)k=(∑i=0kci∗(v∗Ai))k(v*A^n)_k=(v*\sum_{i=0}^kc_iA^i)_k=(\sum_{i=0}^k c_i*(v*A^i))_k(v∗An)k=(v∗∑i=0kciAi)k=(∑i=0kci∗(v∗Ai))k
初始向量 vvv 乘上转移矩阵 iii 次,即为给出的 fif_ifi
于是 ans=∑i=0kci∗fians=\sum_{i=0}^kc_i*f_ians=∑i=0kci∗fi
ccc即特征多项式的系数可以用 AnA^nAn对f(λ)=λk−∑i=0k−1ak−iλif(\lambda)=\lambda^k-\sum_{i=0}^{k-1}a_{k-i}\lambda^if(λ)=λk−∑i=0k−1ak−iλi 多项式取模得出
复杂度 O(k∗log(k)∗log(n))O(k*log(k)*log(n))O(k∗log(k)∗log(n))
主要的思想就是不必求出 AnA^nAn,将AnA^nAn 转换成∑i=0kciAi\sum_{i=0}^k c_iA^i∑i=0kciAi
并且也不求 AiA^iAi,而是用初始向量去乘它∑i=0kci(Ai∗v)\sum_{i=0}^k c_i (A_i*v)∑i=0kci(Ai∗v),得到了我们想要的答案
#include<bits/stdc++.h>
#define N 400050
using namespace std;
typedef long long ll;
const int Mod = 998244353, G = 3;
ll add(ll a, ll b){ return a + b >= Mod ? a + b - Mod : a + b;}
ll mul(ll a, ll b){ return a * b % Mod;}
ll power(ll a, ll b){ ll ans = 1;
for(;b;b>>=1){if(b&1) ans = mul(ans, a); a = mul(a, a);}
return ans;
}
ll inv[N]; int n, k;
#define poly vector<ll>
#define C 20
poly w[C + 1];
int up, bit, rev[N]; ll a[N];
void Init(int len){ up = 1, bit = 0;
while(up < len) up <<= 1, bit++;
for(int i = 0; i < up; i++) rev[i] = (rev[i>>1]>>1)|((i&1)<<(bit-1));
}
void prework(){
inv[0] = inv[1] = 1;
for(int i = 2; i <= N-50; i++) inv[i] = mul(Mod-Mod/i, inv[Mod%i]);
for(int i = 1; i <= C; i++) w[i].resize(1<<(i-1));
ll wn = power(G, (Mod-1)/(1<<C)); w[C][0] = 1;
for(int i = 1; i < (1<<(C-1)); i++) w[C][i] = mul(w[C][i-1], wn);
for(int i = C-1; i; i--)
for(int j = 0; j < (1<<(i-1)); j++)
w[i][j] = w[i+1][j<<1];
}
void NTT(poly &a, int flag){
for(int i = 0; i < up; i++) if(i < rev[i]) swap(a[i], a[rev[i]]);
for(int i = 1, l = 1; i < up; i <<= 1, l++)
for(int j = 0; j < up; j += (i<<1))
for(int k = 0; k < i; k++){
ll x = a[k + j], y = mul(w[l][k], a[k + j + i]);
a[k + j] = add(x, y); a[k + j + i] = add(x, Mod-y);
}
if(flag == -1){
reverse(a.begin() + 1, a.begin() + up);
for(int i = 0; i < up; i++) a[i] = mul(a[i], inv[up]);
}
}
poly operator * (poly a, poly b){
int len = a.size() + b.size() - 1;
Init(len); a.resize(up); b.resize(up);
NTT(a, 1); NTT(b, 1);
for(int i = 0; i < up; i++) a[i] = mul(a[i], b[i]);
NTT(a, -1); a.resize(len); return a;
}
poly Inv(poly a, int len){
poly b(1, power(a[0], Mod - 2)), c;
for(int lim = 4; lim < (len << 2); lim <<= 1){
Init(lim); c = a; c.resize(lim >> 1);
c.resize(up); b.resize(up); NTT(c, 1); NTT(b, 1);
for(int i = 0; i < up; i++) b[i] = mul(b[i], add(2, Mod - mul(b[i], c[i])));
NTT(b, -1); b.resize(lim >> 1);
} b.resize(len); return b;
}
poly operator - (poly a, poly b){
poly c; int len = max(a.size(), b.size()); c.resize(len);
a.resize(len); b.resize(len);
for(int i = 0; i < len; i++) c[i] = add(a[i], Mod - b[i]);
return c;
}
poly operator / (poly a, poly b){
int lim = 1, len = a.size() - b.size() + 1;
reverse(a.begin(), a.end()); reverse(b.begin(), b.end());
while(lim <= len) lim <<= 1;
b = Inv(b, lim); b.resize(len);
a = a * b; a.resize(len);
reverse(a.begin(), a.end());
return a;
}
poly operator % (poly a, poly b){
int len = a.size() - b.size() + 1;
if(len < 0) return a;
poly c = a - (a / b) * b;
c.resize(b.size() - 1); return c;
}
int main(){
scanf("%d%d", &n, &k); prework();
poly f(k + 1);
for(int i = 1; i <= k; i++) scanf("%lld", &f[k - i]), f[k - i] = Mod - (f[k - i] % Mod + Mod) % Mod;
f[k] = 1;
for(int i = 0; i < k; i++) scanf("%lld", &a[i]), a[i] = (a[i] % Mod + Mod) % Mod;
poly res, g;
res.resize(k + 1); res[0] = 1;
g.resize(k + 1); g[1] = 1;
for(;n;n>>=1, g = g * g % f) if(n & 1) res = res * g % f;
ll ans = 0;
for(int i = 0; i < res.size(); i++) ans = add(ans, mul(res[i], a[i]));
cout << ans;
return 0;
}
本文介绍了一种利用矩阵快速幂和多项式取模解决特定递推数列问题的方法,通过构造特征多项式并应用Cayley-Hamilton定理,避免了直接计算高次幂矩阵,降低了算法复杂度。
805

被折叠的 条评论
为什么被折叠?



