[学习笔记]多项式的整除、取模、多点求值和插值及常系数线性递推

一、开头

( WC2019 神犇协会)
undefeatedKO : NOI2017 的题大家都 AK 了吗?
All : AK 了!
ION :我们穿越到 2019 年的 WC 怎么样?
olis :好啊!听说一个弱鸡 xyz32768 要来 WC ,我们一到就把他 D 一遍,这样他 WC2019 不爆零才怪呢!
( WC2019 )
VFN :我们刚刚 A 掉了分身术这题。考虑到你比较菜,我就不用这道题考你了,我换成 Day1 T3 的泳池,如果你做不出来,你就肯定会在 WC2019 我们精心出的试题面前爆零!
xyz32768 :什么??????
NFV :哈哈,没想到你这么菜呀,那我再降低下要求:我告诉你这道题的算法是:常系数线性递推。
xyz32768 :什么?矩乘快速幂??????
phantom : 你这个弱鸡居然连多项式整除和取模都不会,明天你爆零定了!再见!
xyz32768 : 算了,爆零就爆零吧,反正我永远都学不会任何多项式算法。
pool : 没想到 xyz32768 你菜得超出我的眼界了,再见,爆零蒟蒻!

二、前置芝士:多项式求逆

多项式求逆

三、多项式的整除与取模

一个 n n n 次多项式 F ( x ) F(x) F(x) m m m 次多项式 G ( x ) G(x) G(x) ,求多项式 Q ( x ) Q(x) Q(x) R ( x ) R(x) R(x) ,满足:
(1) Q ( x ) Q(x) Q(x) 次数为 n − m n-m nm R ( x ) R(x) R(x) 次数为 m − 1 m-1 m1
(2) F ( x ) = Q ( x ) G ( x ) + R ( x ) F(x)=Q(x)G(x)+R(x) F(x)=Q(x)G(x)+R(x)
这就是求 Q ( x ) Q(x) Q(x) F ( x ) F(x) F(x) G ( x ) G(x) G(x) 整除得到的多项式,且 R ( x ) = F ( x )   m o d   G ( x ) R(x)=F(x)\bmod G(x) R(x)=F(x)modG(x)
下面进入推式子环节。
F ( 1 x ) = Q ( 1 x ) G ( 1 x ) + R ( 1 x ) F(\frac 1x)=Q(\frac 1x)G(\frac 1x)+R(\frac 1x) F(x1)=Q(x1)G(x1)+R(x1)
两边同乘 x n x^n xn
F R ( x ) = Q R ( x ) G R ( x ) + R R ( x ) x n − m + 1 F_R(x)=Q_R(x)G_R(x)+R_R(x)x^{n-m+1} FR(x)=QR(x)GR(x)+RR(x)xnm+1
其中 F R ( x ) F_R(x) FR(x) 表示 F ( x ) F(x) F(x) 的系数翻转,即 F R ( x ) F_R(x) FR(x) i i i 次项系数为 F ( x ) F(x) F(x) n − i n-i ni 次项系数。
F R ( x ) ≡ Q R ( x ) G R ( x ) (   m o d   x n − m + 1 ) F_R(x)\equiv Q_R(x)G_R(x)(\bmod x^{n-m+1}) FR(x)QR(x)GR(x)(modxnm+1)
Q R ( x ) ≡ F R ( x ) × G R − 1 ( x ) (   m o d   x n − m + 1 ) Q_R(x)\equiv F_R(x)\times G^{-1}_R(x)(\bmod x^{n-m+1}) QR(x)FR(x)×GR1(x)(modxnm+1)
需要求 G R ( x ) G_R(x) GR(x) n − m + 1 n-m+1 nm+1 的逆。
至此,我们得到了整除的结果。
取模则更简单:
R ( x ) = F ( x ) − Q ( x ) G ( x ) R(x)=F(x)-Q(x)G(x) R(x)=F(x)Q(x)G(x)
多项式取模的重要应用:如果在一定的条件下 G ( x ) G(x) G(x) 0 0 0 ,那么将计算 F ( x ) F(x) F(x) 改为计算 F ( x )   m o d   G ( x ) F(x)\bmod G(x) F(x)modG(x) 有时可以有效地降低复杂度。

四、应用:多项式多点求值

给定一个 n n n 次多项式 F ( x ) F(x) F(x) m m m 个值 x 1 , x 2 , … , x m x_1,x_2,\dots,x_m x1,x2,,xm ,求出 F ( x 1 ) F(x_1) F(x1) F ( x 2 ) F(x_2) F(x2) … \dots F ( x m ) F(x_m) F(xm)
采用分治的算法。取 m i d = ⌊ m 2 ⌋ mid=\lfloor\frac m2\rfloor mid=2m
先计算 G 1 ( x ) = ∏ i = 1 m i d ( x − x i ) G_1(x)=\prod_{i=1}^{mid}(x-x_i) G1(x)=i=1mid(xxi) G 2 ( x ) = ∏ i = m i d + 1 m ( x − x i ) G_2(x)=\prod_{i=mid+1}^m(x-x_i) G2(x)=i=mid+1m(xxi)
那么:
(1)对于任意的 1 ≤ k ≤ m i d 1\le k\le mid 1kmid
G 1 ( x k ) = ( x k − x k ) ∏ i = 1 , i ≠ k m i d ( x k − x i ) = 0 G_1(x_k)=(x_k-x_k)\prod_{i=1,i\ne k}^{mid}(x_k-x_i)=0 G1(xk)=(xkxk)i=1,i̸=kmid(xkxi)=0
(2)对于任意的 m i d < k ≤ m mid<k\le m mid<km
G 2 ( x k ) = ( x k − x k ) ∏ i = m i d + 1 , i ≠ k m ( x k − x i ) = 0 G_2(x_k)=(x_k-x_k)\prod_{i=mid+1,i\ne k}^m(x_k-x_i)=0 G2(xk)=(xkxk)i=mid+1,i̸=km(xkxi)=0
所以可以分别将 F ( x ) F(x) F(x) 转换成 F ( x )   m o d   G 1 ( x ) F(x)\bmod G_1(x) F(x)modG1(x) F ( x )   m o d   G 2 ( x ) F(x)\bmod G_2(x) F(x)modG2(x)
所以,设 c a l c ( F ( x ) , S ) calc(F(x),S) calc(F(x),S) 为计算 F ( x ) F(x) F(x) 取集合 S S S 内每个数时多项式的值:
c a l c ( F ( x ) , x 1 , 2 , … , m ) calc(F(x),x_{1,2,\dots,m}) calc(F(x),x1,2,,m)
= c a l c ( F ( x )   m o d   G 1 ( x ) , x 1 , 2 , … , m i d ) =calc(F(x)\bmod G_1(x),x_{1,2,\dots,mid}) =calc(F(x)modG1(x),x1,2,,mid)
+ c a l c ( F ( x )   m o d   G 2 ( x ) , x m i d + 1 , m i d + 2 , … , m i d ) +calc(F(x)\bmod G_2(x),x_{mid+1,mid+2,\dots,mid}) +calc(F(x)modG2(x),xmid+1,mid+2,,mid)
G 1 , G 2 G_1,G_2 G1,G2 可以分治 FFT 求得。
复杂度有:
T ( n ) = T ( n 2 ) + O ( n log ⁡ n ) T(n)=T(\frac n2)+O(n\log n) T(n)=T(2n)+O(nlogn)
由主定理得:
T ( n ) = O ( n log ⁡ 2 n ) T(n)=O(n\log^2n) T(n)=O(nlog2n)

五、应用:多项式快速插值

给定 ( x 1 , y 1 ) ( x 2 , y 2 ) … ( x n + 1 , y n + 1 ) (x_1,y_1)(x_2,y_2)\dots(x_{n+1},y_{n+1}) (x1,y1)(x2,y2)(xn+1,yn+1) ,求一个多项式 F ( x ) F(x) F(x) 使得对于任何一个 1 ≤ i ≤ n + 1 1\le i\le n+1 1in+1 都有:
F ( x i ) = y i F(x_i)=y_i F(xi)=yi
众所周知,通过 Lagrange 拉格朗日插值公式:
F ( x ) ∑ i = 1 n + 1 y i ∏ j = 1 , j ≠ i n + 1 x − x j x i − x j F(x)\sum_{i=1}^{n+1}y_i\prod_{j=1,j\ne i}^{n+1}\frac{x-x_j}{x_i-x_j} F(x)i=1n+1yij=1,j̸=in+1xixjxxj
可以得到 O ( n 2 ) O(n^2) O(n2) 的算法。
考虑把 Lagrange 插值的式子转化一下:
F ( x ) = ∑ i = 1 n + 1 y i ∏ j = 1 , j ≠ i n + 1 ( x i − x j ) ∏ j = 1 , j ≠ i n + 1 ( x − x j ) F(x)=\sum_{i=1}^{n+1}\frac{y_i}{\prod_{j=1,j\ne i}^{n+1}(x_i-x_j)}\prod_{j=1,j\ne i}^{n+1}(x-x_j) F(x)=i=1n+1j=1,j̸=in+1(xixj)yij=1,j̸=in+1(xxj)
G ( x ) = ∏ j = 1 n + 1 ( x − x j ) G(x)=\prod_{j=1}^{n+1}(x-x_j) G(x)=j=1n+1(xxj) (可以用分治 FFT 求出)。
∏ j = 1 , j ≠ i n + 1 ( x i − x j ) = ( G ( x ) x − x i ) ( x i ) \prod_{j=1,j\ne i}^{n+1}(x_i-x_j)=(\frac{G(x)}{x-x_i})(x_i) j=1,j̸=in+1(xixj)=(xxiG(x))(xi)
记为 G i ( x i ) G_i(x_i) Gi(xi)
G ( x ) = ( x − x i ) G i ( x ) G(x)=(x-x_i)G_i(x) G(x)=(xxi)Gi(x)
求导:
G ′ ( x ) = G i ( x ) + ( x − x i ) G i ′ ( x ) G'(x)=G_i(x)+(x-x_i)G_i'(x) G(x)=Gi(x)+(xxi)Gi(x)
x = x i x=x_i x=xi ,得:
G i ( x i ) = G ′ ( x i ) G_i(x_i)=G'(x_i) Gi(xi)=G(xi)
于是转化成了多点求值。
于是转化成:
∑ i = 1 n + 1 y i G ′ ( x i ) × G ( x ) x − x i \sum_{i=1}^{n+1}\frac{y_i}{G'(x_i)}\times\frac{G(x)}{x-x_i} i=1n+1G(xi)yi×xxiG(x)
可以用分治 FFT 求得。
具体地,设 m i d = ⌊ n + 1 2 ⌋ mid=\lfloor\frac{n+1}2\rfloor mid=2n+1 L ( x ) = ∏ i = 1 m i d ( x − x i ) L(x)=\prod_{i=1}^{mid}(x-x_i) L(x)=i=1mid(xxi) R ( x ) = ∏ i = m i d + 1 n + 1 ( x − x i ) R(x)=\prod_{i=mid+1}^{n+1}(x-x_i) R(x)=i=mid+1n+1(xxi)
那么设 q i = y i G ′ ( x i ) q_i=\frac{y_i}{G'(x_i)} qi=G(xi)yi ,则上式为:
R ( x ) ∑ i = 1 m i d q i ∏ j = 1 , j ≠ i m i d ( x − x j ) + L ( x ) ∑ i = m i d + 1 n + 1 q i ∏ j = m i d + 1 , j ≠ i n + 1 ( x − x j ) R(x)\sum_{i=1}^{mid}q_i\prod_{j=1,j\ne i}^{mid}(x-x_j)+L(x)\sum_{i=mid+1}^{n+1}q_i\prod_{j=mid+1,j\ne i}^{n+1}(x-x_j) R(x)i=1midqij=1,j̸=imid(xxj)+L(x)i=mid+1n+1qij=mid+1,j̸=in+1(xxj)
递归即可。
复杂度和多点求值一样是 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)
神仙 zzq 的博客:
orz zzq

六、应用:常系数线性递推

一、引入

地球上的 OIer 都知道 Fibonacci 数列:
f ( 0 ) = 0 f(0)=0 f(0)=0
f ( n ) = f ( n − 1 ) + f ( n − 2 ) ( n ≥ 1 ) f(n)=f(n-1)+f(n-2)(n\ge 1) f(n)=f(n1)+f(n2)(n1)
n n n 比较小时可以递推。
n n n 规模比较大,如 1 0 9 10^9 109 甚至 1 0 18 10^{18} 1018 时,可以利用矩阵乘法优化。
而假设由一个 k k k 阶递推数列满足:
a n = ∑ i = 1 k f i × a k − i a_n=\sum_{i=1}^kf_i\times a_{k-i} an=i=1kfi×aki
a 0... k − 1 a_{0...k-1} a0...k1 f 1... k f_{1...k} f1...k 是已知的。
这是一个 k k k 阶的递推数列。
如果暴力转移,则复杂度 O ( n k ) O(nk) O(nk)
如果使用矩阵乘法,则复杂度 O ( k 3 log ⁡ n ) O(k^3\log n) O(k3logn)
n ≤ 1 0 9 n\le 10^9 n109 k ≤ 32000 k\le 32000 k32000 时,
就需要用到一个线性代数黑科技——常系数线性递推。

二、求法

假设转移矩阵为 A A A ,初始列向量:
S t = [ a 0 a 1 ⋮ a k − 1 ] St=\begin{bmatrix}a_0\\a_1\\\vdots\\a_{k-1}\end{bmatrix} St=a0a1ak1
则:
a n = A n × S t a_n=A^n\times St an=An×St
假设我们构造了一个序列 c c c 使得:
A n = ∑ i = 0 k − 1 c i A i A^n=\sum_{i=0}^{k-1}c_iA^i An=i=0k1ciAi
那么两边同乘 S t St St 可得:
a n = A n × S t = ∑ i = 0 k − 1 c i A i × S t = ∑ i = 0 k − 1 c i S t i a_n=A^n\times St=\sum_{i=0}^{k-1}c_iA^i\times St=\sum_{i=0}^{k-1}c_iSt_i an=An×St=i=0k1ciAi×St=i=0k1ciSti

三、构造序列 c c c

考虑 c c c 的生成函数 C ( A ) C(A) C(A) ,即 C C C 是一个以矩阵为参数的多项式。
假设有
A n = F ( A ) G ( A ) + C ( A ) A^n=F(A)G(A)+C(A) An=F(A)G(A)+C(A)
其中 G ( A ) G(A) G(A) 的次数为 k k k
如果 G ( A ) = [ 0 0 … 0 0 0 … 0 ⋮ ⋮ ⋱ ⋮ 0 0 … 0 ] G(A)=\begin{bmatrix}0&0&\dots&0\\0&0&\dots&0\\\vdots&\vdots&\ddots&\vdots\\0&0&\dots&0\end{bmatrix} G(A)=000000000 ,那么就有:
C ( A ) = A n   m o d   G ( A ) C(A)=A^n\bmod G(A) C(A)=AnmodG(A)
用快速幂+多项式取模就能做到 O ( k log ⁡ k log ⁡ n ) O(k\log k\log n) O(klogklogn) 的复杂度。
G ( A ) G(A) G(A) 为矩阵 A A A特征多项式

四、特征值和特征向量

以下把全 0 0 0 的矩阵简写为 0 0 0
如果存在数 λ \lambda λ 和列向量 X X X 满足:
A X = λ X AX=\lambda X AX=λX

( λ I − A ) X = 0 (\lambda I-A)X=0 (λIA)X=0
I I I 为单位矩阵)
那么 λ \lambda λ X X X 分别为矩阵 A A A 的特征值和特征向量。
一个 k k k 阶的满秩矩阵有 k k k 组特征值和特征向量。

五、Cayley-Hamilton( C-H )定理.

定理:
∏ i ( λ i I − A ) = 0 \prod_i(\lambda_i I-A)=0 i(λiIA)=0
其中 λ i \lambda_i λi A A A 的第 i i i 个特征值。
为了证明这个定理,我们不妨先证明上式乘上任意特征向量等于 0 0 0 矩阵。
先证明:
( λ I − A ) ( ω I − A ) = λ ω I 2 − λ I A − ω I A + A 2 = ( ω I − A ) ( λ I − A ) (\lambda I-A)(\omega I-A)=\lambda\omega I^2-\lambda IA-\omega IA+A^2=(\omega I-A)(\lambda I-A) (λIA)(ωIA)=λωI2λIAωIA+A2=(ωIA)(λIA)
然后我们能证明乘上 X j X_j Xj 0 0 0 矩阵:
( ∏ i ( λ i I − A ) ) × X i = ( ∏ i ≠ j ( λ i I − A ) ) × ( ( λ j I − A ) × X j ) = 0 (\prod_i(\lambda_i I-A))\times X_i=(\prod_{i\ne j}(\lambda_i I-A))\times((\lambda_j I-A)\times X_j)=0 (i(λiIA))×Xi=(i̸=j(λiIA))×((λjIA)×Xj)=0
于是我们证明了 C-H 定理。

六、求特征多项式

一个奇怪的结论:
f ( λ ) = ∣ λ I − A ∣ f(\lambda)=|\lambda I-A| f(λ)=λIA
∣ A ∣ |A| A 为矩阵 A A A 的行列式)
( − 1 ) k ∏ i ( λ i I − A ) (-1)^k\prod_i(\lambda_i I-A) (1)ki(λiIA) 的系数相同。
换句话说, f ( λ ) = ∣ λ I − A ∣ f(\lambda)=|\lambda I-A| f(λ)=λIA 就是矩阵 A A A 的特征多项式 G ( x ) G(x) G(x)
你可能会问: f f f 的参数和 ∏ i ( λ i I − A ) \prod_i(\lambda_i I-A) i(λiIA) 的参数不一样,为什么系数会相同?
先假设 ( − 1 ) k ∏ i ( λ i I − A ) (-1)^k\prod_i(\lambda_i I-A) (1)ki(λiIA) 的参数不是矩阵而是数。而单位矩阵相当于 1 1 1 ,所以先将其视为 ( − 1 ) k ∏ i ( λ i − λ ) (-1)^k\prod_i(\lambda_i-\lambda) (1)ki(λiλ)
我们可以发现,将 k k k 个特征值 λ 1 , λ 2 , … , λ k \lambda_1,\lambda_2,\dots,\lambda_k λ1,λ2,,λk 代入这两个多项式都会使多项式的值为 0 0 0 ,并且这两个多项式的 λ k \lambda^k λk 项相同。
有了这 k + 1 k+1 k+1 个条件,我们得出这两个多项式的系数相同。
( − 1 ) k (-1)^k (1)k 实际上可以忽略(因为我们只关心 G ( A ) G(A) G(A) 是否为 0 0 0 矩阵,而 0 0 0 矩阵取反后还是 0 0 0 矩阵)。
考虑矩阵 λ I − A \lambda I-A λIA
[ λ − 1 0 0 0 . . . 0 0 λ − 1 0 0 . . . 0 0 0 λ − 1 0 . . . 0 0 0 0 λ − 1 . . . 0 . . . . . . . . . . . . . . . . . . . . .   − f k − f k − 1 . − f k − 2 − f k − 3 − f k − 4 . . . λ − f 1 ] \begin{bmatrix}\lambda&-1&0&0&0&...&0\\0&\lambda&-1&0&0&...&0\\0&0&\lambda&-1&0&...&0\\0&0&0&\lambda&-1&...&0\\...&...&...&...&...&...&...\\\ -f_k&-f_{k-1}&.-f_{k-2}&-f_{k-3}&-f_{k-4}&...&\lambda-f_1\\\end{bmatrix} λ000... fk1λ00...fk101λ0....fk2001λ...fk30001...fk4..................0000...λf1
手算行列式得到:
∣ λ I − A ∣ = λ k − ∑ i = 1 k f i λ k − i |\lambda I-A|=\lambda^k-\sum_{i=1}^kf_i\lambda^{k-i} λIA=λki=1kfiλki
所以我们得出特征多项式:
g k = 1 g_k=1 gk=1
g i = − f k − i ( 0 ≤ i < k ) g_i=-f_{k-i}(0\le i<k) gi=fki(0i<k)
开头中提到的 NOI2017 Day1 T3 泳池 就是一个常系数线性递推。
虽然那题多项式暴力取模能过

七、模板

多项式多点求值和快速插值代码暂无,见谅。
一、多项式整除与取模

#include 
#include 
#include 
#include 
#include 
#define For(i, a, b) for (i = a; i <= b; i++)
#define Step(i, a, b, x) for (i = a; i <= b; i += x)
#define Pow(k, n) for (k = 1; k < n; k <<= 1)
using namespace std;
inline int read() {
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}
const int N = 7e5 + 5, ZZQ = 998244353;
int n, m, tot = 2, f[N], g[N], rf[N], rg[N], invg[N], rev[N],
tmp1[N], tmp2[N], gp[N], ff, quot[N], xmod[N];
int qpow(int a, int b) {
	int res = 1;
	while (b) {
		if (b & 1) res = 1ll * res * a % ZZQ;
		a = 1ll * a * a % ZZQ;
		b >>= 1;
	}
	return res;
}
void FFT(int n, int *a, int op) {
	int i, j, k, sp = n;
	For (i, 0, n - 1) if (i < rev[i]) swap(a[i], a[rev[i]]);
	gp[n] = qpow(op == 1 ? 3 : 332748118, (ZZQ - 1) / n);
	For (i, 1, tot) sp >>= 1, gp[sp] =
		1ll * gp[sp << 1] * gp[sp << 1] % ZZQ;
	Pow(k, n) {
		int x = gp[k << 1];
		Step (i, 0, n - 1, k << 1) {
			int w = 1;
			For (j, 0, k - 1) {
				int u = a[i + j], v = 1ll * w * a[i + j + k] % ZZQ;
				a[i + j] = (u + v) % ZZQ;
				a[i + j + k] = (u - v + ZZQ) % ZZQ;
				w = 1ll * w * x % ZZQ;
			}
		}
	}
}
int main() {
	int i, k, orz;
	n = read(); m = read();
	For (i, 0, n) f[i] = rf[n - i] = read();
	For (i, 0, m) g[i] = rg[m - i] = read();
	invg[0] = qpow(rg[0], ZZQ - 2);
	Pow(k, n - m + 1) {
		orz = qpow(k << 2, ZZQ - 2);
		For (i, 0, (k << 2) - 1)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
		For (i, 0, (k << 1) - 1) tmp1[i] = rg[i];
		For (i, (k << 1), (k << 2) - 1) tmp1[i] = 0;
		For (i, 0, k - 1) tmp2[i] = invg[i];
		For (i, k, (k << 2) - 1) tmp2[i] = 0;
		FFT(k << 2, tmp1, 1); FFT(k << 2, tmp2, 1);
		For (i, 0, (k << 2) - 1)
			tmp1[i] = 1ll * tmp1[i] * tmp2[i] % ZZQ;
		For (i, 0, (k << 2) - 1)
			tmp1[i] = (2 - tmp1[i] + ZZQ) % ZZQ;
		For (i, 0, k - 1) tmp2[i] = invg[i];
		For (i, k, (k << 2) - 1) tmp2[i] = 0;
		FFT(k << 2, tmp2, 1);
		For (i, 0, (k << 2) - 1)
			invg[i] = 1ll * tmp1[i] * tmp2[i] % ZZQ;
		FFT(k << 2, invg, -1);
		For (i, 0, (k << 2) - 1) invg[i] = 1ll * invg[i] * orz % ZZQ;
		For (i, (k << 1), (k << 2) - 1) invg[i] = 0;
		tot++;
	}
	memset(tmp1, 0, sizeof(tmp1));
	memset(tmp2, 0, sizeof(tmp2));
	For (i, 0, n) tmp1[i] = rf[i];
	For (i, 0, n - m) tmp2[i] = invg[i];
	ff = 1; tot = 0;
	while (ff <= (n << 1) - m) ff <<= 1, tot++;
	orz = qpow(ff, ZZQ - 2);
	For (i, 0, ff - 1)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
	FFT(ff, tmp1, 1); FFT(ff, tmp2, 1);
	For (i, 0, ff - 1) quot[i] = 1ll * tmp1[i] * tmp2[i] % ZZQ;
	FFT(ff, quot, -1);
	For (i, 0, ff - 1) quot[i] = 1ll * quot[i] * orz % ZZQ;
	For (i, 0, n - m) if (i < n - m - i)
		swap(quot[i], quot[n - m - i]);
	For (i, n - m + 1, ff - 1) quot[i] = 0;
	For (i, 0, n - m) printf("%d ", quot[i]); cout << endl;
	ff = 1; tot = 0;
	while (ff <= n) ff <<= 1, tot++;
	orz = qpow(ff, ZZQ - 2);
	For (i, 0, ff - 1)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
	FFT(ff, g, 1); FFT(ff, quot, 1);
	For (i, 0, ff - 1) xmod[i] = 1ll * g[i] * quot[i] % ZZQ;
	FFT(ff, xmod, -1);
	For (i, 0, ff - 1) xmod[i] = 1ll * xmod[i] * orz % ZZQ;
	For (i, 0, n) xmod[i] = (f[i] - xmod[i] + ZZQ) % ZZQ;
	For (i, 0, m - 1) printf("%d ", xmod[i]); cout << endl;
	return 0;
}

二、常系数线性递推

#include 
#include 
#include 
#include 
#include 
#define For(i, a, b) for (i = a; i <= b; i++)
#define Step(i, a, b, x) for (i = a; i <= b; i += x)
#define Pow(k, n) for (k = 1; k < n; k <<= 1)
using namespace std;

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
void Swap(T &a, T &b) {a ^= b; b ^= a; a ^= b;}

const int N = 5e5 + 5, ZZQ = 998244353;

int n, K, f[N], invf[N], orz[N], a[N], rev[N], tmp1[N], tmp2[N], df[N],
ff = 4, tot = 2, gg, res[N], bas[N], ans;

int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1) res = 1ll * res * a % ZZQ;
		a = 1ll * a * a % ZZQ;
		b >>= 1;
	}
	return res;
}

void FFT(int n, int *a, int op)
{
	int i, j, k;
	For (i, 0, n - 1) if (i < rev[i]) Swap(a[i], a[rev[i]]);
	orz[k = n >> 1, n] = qpow(3, op == 1 ? (ZZQ - 1) / n :
		(ZZQ - 1) / n * (n - 1));
	while (k) orz[k] = 1ll * orz[k << 1] * orz[k << 1] % ZZQ, k >>= 1;
	Pow(k, n)
	{
		int x = orz[k << 1];
		Step (i, 0, n - 1, k << 1)
		{
			int w = 1;
			For (j, 0, k - 1)
			{
				int u = a[i + j], v = 1ll * w * a[i + j + k] % ZZQ;
				a[i + j] = (u + v) % ZZQ;
				a[i + j + k] = (u - v + ZZQ) % ZZQ;
				w = 1ll * w * x % ZZQ;
			}
		}
	}
}

void get_mod(int *f)
{
	int i;
	For (i, 0, K << 1) tmp1[i] = f[(K << 1) - i];
	For (i, (K << 1) + 1, ff - 1) tmp1[i] = 0;
	FFT(ff, tmp1, 1);
	For (i, 0, ff - 1) tmp1[i] = 1ll * tmp1[i] * invf[i] % ZZQ;
	FFT(ff, tmp1, -1);
	For (i, 0, ff - 1) tmp1[i] = 1ll * tmp1[i] * gg % ZZQ;
	For (i, K + 1, ff - 1) tmp1[i] = 0;
	For (i, 0, K - 1 >> 1) Swap(tmp1[i], tmp1[K - i]);
	FFT(ff, tmp1, 1);
	For (i, 0, ff - 1) tmp1[i] = 1ll * tmp1[i] * df[i] % ZZQ;
	FFT(ff, tmp1, -1);
	For (i, 0, ff - 1) f[i] = (f[i] - 1ll * tmp1[i] * gg % ZZQ + ZZQ) % ZZQ;
	For (i, K + 1, ff - 1) f[i] = 0;
}

int main()
{
	int i, k;
	n = read(); K = read();
	For (i, 1, K) f[i] = df[K - i] = (ZZQ - read() % ZZQ) % ZZQ;
	f[0] = df[K] = 1;
	For (i, 0, K - 1) a[i] = (read() % ZZQ + ZZQ) % ZZQ;
	invf[0] = 1;
	Pow(k, K + 1)
	{
		gg = qpow(ff, ZZQ - 2);
		For (i, 0, ff - 1)
			rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
		For (i, 0, (k << 1) - 1)
			tmp1[i] = f[i], tmp1[i + (k << 1)] = 0;
		For (i, 0, k - 1)
			tmp2[i] = invf[i], tmp2[i + k] = tmp2[i + (k << 1)]
			= tmp2[i + k * 3] = 0;
		FFT(ff, tmp1, 1); FFT(ff, tmp2, 1);
		For (i, 0, ff - 1) tmp1[i] =
			(2 - 1ll * tmp1[i] * tmp2[i] % ZZQ + ZZQ) % ZZQ;
		For (i, 0, k - 1)
			tmp2[i] = invf[i], tmp2[i + k] = tmp2[i + (k << 1)]
			= (tmp2[i + k * 3]) = 0;
		FFT(ff, tmp2, 1);
		For (i, 0, ff - 1) invf[i] = 1ll * tmp1[i] * tmp2[i] % ZZQ;
		FFT(ff, invf, -1);
		For (i, 0, ff - 1) invf[i] = 1ll * invf[i] * gg % ZZQ;
		For (i, K + 1, ff - 1) invf[i] = 0;
		ff <<= 1; tot++;
	}
	ff = 1; tot = 0;
	while (ff <= K * 3) ff <<= 1, tot++;
	For (i, 0, ff - 1)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);
	gg = qpow(ff, ZZQ - 2);
	FFT(ff, invf, 1);
	FFT(ff, df, 1);
	bas[res[0] = 1] = 1;
	while (n)
	{
		if (n & 1)
		{
			FFT(ff, res, 1);
			For (i, 0, ff - 1) tmp1[i] = bas[i];
			FFT(ff, tmp1, 1);
			For (i, 0, ff - 1) res[i] = 1ll * res[i] * tmp1[i] % ZZQ;
			FFT(ff, res, -1);
			For (i, 0, ff - 1) res[i] = 1ll * res[i] * gg % ZZQ;
			get_mod(res);
		}
		FFT(ff, bas, 1);
		For (i, 0, ff - 1) bas[i] = 1ll * bas[i] * bas[i] % ZZQ;
		FFT(ff, bas, -1);
		For (i, 0, ff - 1) bas[i] = 1ll * bas[i] * gg % ZZQ;
		get_mod(bas);
		n >>= 1;
	}
	For (i, 0, K - 1) ans = (ans + 1ll * a[i] * res[i] % ZZQ) % ZZQ;
	cout << ans << endl;
	return 0;
}

你可能感兴趣的:(学习笔记)