前置知识

多项式除法|取模

该部分见 多项式操作

特征值与特征向量

(这部分我也不怎么理解,就大概记个结论)

n 阶矩阵 A 的特征向量 v 与,有特征值 λ 满足
Av=λv
移项则有 (λIA)v=0,其中 I 为单位矩阵

那么有解的充要条件是 det(λIA)=0

f(λ)=det(λIA),即此时可将 det(λIA) 看作一个 n 阶多项式,则称 f(λ) 为矩阵 A 的特征多项式,对矩阵 A,其任意特征值 λ0 都满足 f(λ0)=0

Hamiton-Cayley定理

对矩阵 A 及其特征多项式 f(x),满足 f(A)=O,其中 O 为零矩阵

证明留坑

常系数齐次线性递推

求一个满足 k 阶齐次线性递推数列 hi 的第 n 项,即
hn=i=1kfi×hni
它有三个特点

  • 常系数:递推系数与下标无关
  • 齐次:递推式不存在常数项,就类似 y=ax+bb=0
  • 线性:都为一次项

矩阵快速幂

加速矩阵 A × 初始矩阵 H
(f1f2f3fn100001000001)×(hkh3h2h1)=(hk+1h4h3h2)
那么快速幂 AnH 即可,复杂度 O(k3logn)

优化

考虑 Av=λv,也就是说现在存在
(hk+1h4h3h2)=(λhkλh3λh2λh1)
推广一下,hn=λn1h1,则
hk+1=f1hk1+f2hk2++fkh1λkh1=f1λk1h1+f2λk2h2++fkh1λk=f1λk1+f2λk2++fk
λ 替换为 x,则有 F(x)=xkf1xk1f2xk2fk

此时 F(x) 就是矩阵 A 的特征多项式,有 F(A)=0

考虑等式
xn=F(x)Q(x)+R(x)
A 代入,则有
An=R(A)
别忘了现在要求的是 Anv,那么两边同乘 v,有 Anv=R(A)v

R(x) 可以通过由 A(x)=xnF(x) 取模求得,注意此时不能直接取模,因为 n 可以非常大(比如 n=1e9,无法存下多项式 xn,需要通过快速幂求解

R(x)=i=0k1cixi,则
Anv=i=0k1ciAiv
Aiv 实际上就是
(hk+ihi+3hi+2hi+1)
A 的次数最多 k1,也就是说最多只会取到前 2k1hi,我们最后要取得是 Anv 的第一位,每次乘上一个 A 则令 h 数组向下移一位,那么最终答案即为 ans=i=0k1cihi

时间复杂度 O(klogklogn)

其实每次在求多项式取模的时候都是对 F(x) 取模,所以可以提前对 F(x) 预处理,求出其逆,那么可以少很多常数(虽然我的常数还是贼大就是了。。)

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

#define MOD 998244353
#define g 3

using namespace std;

typedef long long LL;

const int MAXK = 32000 + 10;
const int MAXN = 1 << 20;

inline LL power (LL x, int p) {
LL cnt = 1;
while (p) {
if (p & 1) cnt = cnt * x % MOD;
x = x * x % MOD;
p >>= 1;
}
return cnt;
}
const LL invg = power (g, MOD - 2);

int oppo[MAXN]= {0}, limit;
void NTT (LL* a, int inv) {
for (int i = 0; i < limit; i ++)
if (i < oppo[i])
swap (a[i], a[oppo[i]]);
for (int mid = 1; mid < limit; mid <<= 1) {
LL ome = power (inv == 1 ? g : invg, (MOD - 1) / (mid << 1));
for (int n = mid << 1, j = 0; j < limit; j += n) {
LL x = 1;
for (int k = 0; k < mid; k ++, x = x * ome % MOD) {
LL a1 = a[j + k], xa2 = x * a[j + mid + k] % MOD;
a[j + k] = (a1 + xa2) % MOD;
a[j + mid + k] = (a1 - xa2 + MOD) % MOD;
}
}
}
}
LL A[MAXN], B[MAXN], O[MAXN]= {0};
void mul (LL* X, LL* Y, int fn, int fm, int rst) {
int n, lim;
for (n = 1, lim = 0; n <= fn + fm; n <<= 1, lim ++);
limit = n;
for (int i = 0; i < limit; i ++) oppo[i] = (oppo[i >> 1] >> 1) | ((i & 1) << (lim - 1));
for (int i = 0; i < limit; i ++) A[i] = B[i] = 0;
for (int i = 0; i <= fn; i ++) A[i] = X[i];
for (int i = 0; i <= fm; i ++) B[i] = Y[i];
NTT (A, 1), NTT (B, 1);
for (int i = 0; i < limit; i ++) A[i] = A[i] * B[i] % MOD;
NTT (A, - 1);
LL invn = power (n, MOD - 2);
for (int i = 0; i <= rst; i ++) X[i] = A[i] * invn % MOD;
for (int i = rst + 1; i <= fn + fm; i ++) X[i] = 0;
}
void inverse (int deg, LL* a) {
if (deg == 1) { a[0] = power (O[0], MOD - 2); return ; }
inverse ((deg + 1) >> 1, a);
int n, lim;
for (n = 1, lim = 0; n <= deg << 1; n <<= 1, lim ++);
limit = n;
for (int i = 0; i < limit; i ++) oppo[i] = (oppo[i >> 1] >> 1) | ((i & 1) << (lim - 1));
for (int i = 0; i < limit; i ++) A[i] = B[i] = 0;
for (int i = 0; i < deg; i ++) A[i] = O[i];
for (int i = 0; i < deg << 1; i ++) B[i] = a[i];
NTT (A, 1), NTT (B, 1);
for (int i = 0; i < limit; i ++) B[i] = B[i] * ((2ll - A[i] * B[i] % MOD + MOD) % MOD) % MOD;
NTT (B, - 1);
LL invn = power (n, MOD - 2);
for (int i = 0; i < deg; i ++) a[i] = B[i] * invn % MOD;
}
LL R[MAXN], INVG[MAXN]= {0};
void prep (LL* G, int n, int m) {
for (int i = 0; i <= m; i ++) O[i] = G[i];
reverse (O, O + m + 1);
for (int i = n - m + 2; i <= m; i ++) O[i] = 0;
inverse (n - m + 1, INVG);
}
void PolyMod (LL* F, LL* G, int n, int m){ // F mod G = R
for (int i = 0; i <= n; i ++) R[i] = F[i];
reverse (R, R + n + 1); mul (R, INVG, n, n - m + 1, n);
reverse (R, R + n - m + 1);
for (int i = n - m + 1; i <= n; i ++) R[i] = 0;
mul (R, G, n - m, m, n);
for (int i = 0; i < m; i ++) R[i] = (F[i] - R[i] + MOD) % MOD;
for (int i = 0; i <= n; i ++) { F[i] = i < m ? R[i] : 0; R[i] = 0; }
}

int N, K, L;
LL F[MAXK]= {0}, h[MAXK]= {0};

LL X[MAXN]= {0}, ANS[MAXN]= {0};
void PolyPow (int p) { // X ^ p (mod F)
while (p) {
if (p & 1) { mul (ANS, X, K - 1, K - 1, L); PolyMod (ANS, F, L, K); }
mul (X, X, K - 1, K - 1, L); PolyMod (X, F, L, K);
p >>= 1;
}
}

inline int getnum () {
int num = 0; char ch = getchar ();
bool isneg = false;
while (! isdigit (ch)) {
if (ch == '-') isneg = true;
ch = getchar ();
}
while (isdigit (ch)) num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
return isneg ? - num : num;
}

int main () {
N = getnum (), K = getnum (); L = (K - 1) << 1;
for (int i = 1; i <= K; i ++) F[K - i] = (MOD - getnum () % MOD) % MOD;
for (int i = 0; i < K; i ++) h[i] = getnum () % MOD;
F[K] = X[1] = ANS[0] = 1; prep (F, L, K);
PolyPow (N);
LL ans = 0;
for (int i = 0; i < K; i ++) ans = (ans + ANS[i] * h[i] % MOD + MOD) % MOD;
cout << ans << endl;

return 0;
}

常系数非齐次线性递推

求一个满足 k 阶齐次线性递推数列 hi 的第 n 项,即
hn=P(n)+i=1kfi×hni