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

一、开头

( 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 次多项式 F ( x ) F(x) m m 次多项式 G ( x ) G(x) ,求多项式 Q ( x ) Q(x) R ( x ) R(x) ,满足:
(1) Q ( x ) Q(x) 次数为 n m n-m R ( x ) R(x) 次数为 m 1 m-1
(2) F ( x ) = Q ( x ) G ( x ) + R ( x ) F(x)=Q(x)G(x)+R(x)
这就是求 Q ( x ) Q(x) F ( x ) F(x) G ( x ) G(x) 整除得到的多项式,且 R ( x ) = F ( x )   m o d   G ( x ) R(x)=F(x)\bmod G(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)
两边同乘 x n x^n
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}
其中 F R ( x ) F_R(x) 表示 F ( x ) F(x) 的系数翻转,即 F R ( x ) F_R(x) i i 次项系数为 F ( x ) F(x) n i n-i 次项系数。
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})
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})
需要求 G R ( x ) G_R(x) n m + 1 n-m+1 的逆。
至此,我们得到了整除的结果。
取模则更简单:
R ( x ) = F ( x ) Q ( x ) G ( x ) R(x)=F(x)-Q(x)G(x)
多项式取模的重要应用:如果在一定的条件下 G ( x ) G(x) 0 0 ,那么将计算 F ( x ) F(x) 改为计算 F ( x )   m o d   G ( x ) F(x)\bmod G(x) 有时可以有效地降低复杂度。

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

给定一个 n n 次多项式 F ( x ) F(x) m m 个值 x 1 , x 2 , , x m x_1,x_2,\dots,x_m ,求出 F ( x 1 ) F(x_1) F ( x 2 ) F(x_2) \dots F ( x m ) F(x_m)
采用分治的算法。取 m i d = m 2 mid=\lfloor\frac m2\rfloor
先计算 G 1 ( x ) = i = 1 m i d ( x x i ) G_1(x)=\prod_{i=1}^{mid}(x-x_i) G 2 ( x ) = i = m i d + 1 m ( x x i ) G_2(x)=\prod_{i=mid+1}^m(x-x_i)
那么:
(1)对于任意的 1 k m i d 1\le k\le mid
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
(2)对于任意的 m i d < k m mid<k\le m
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
所以可以分别将 F ( x ) F(x) 转换成 F ( x )   m o d   G 1 ( x ) F(x)\bmod G_1(x) F ( x )   m o d   G 2 ( x ) F(x)\bmod G_2(x)
所以,设 c a l c ( F ( x ) , S ) calc(F(x),S) 为计算 F ( x ) F(x) 取集合 S S 内每个数时多项式的值:
c a l c ( F ( x ) , x 1 , 2 , , m ) calc(F(x),x_{1,2,\dots,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})
+ 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})
G 1 , G 2 G_1,G_2 可以分治 FFT 求得。
复杂度有:
T ( n ) = T ( n 2 ) + O ( n log n ) T(n)=T(\frac n2)+O(n\log n)
由主定理得:
T ( n ) = O ( n log 2 n ) T(n)=O(n\log^2n)

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

给定 ( 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}) ,求一个多项式 F ( x ) F(x) 使得对于任何一个 1 i n + 1 1\le i\le n+1 都有:
F ( x i ) = y i F(x_i)=y_i
众所周知,通过 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}
可以得到 O ( n 2 ) O(n^2) 的算法。
考虑把 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)
G ( x ) = j = 1 n + 1 ( x x j ) G(x)=\prod_{j=1}^{n+1}(x-x_j) (可以用分治 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)
记为 G i ( x i ) G_i(x_i)
G ( x ) = ( x x i ) G i ( x ) G(x)=(x-x_i)G_i(x)
求导:
G ( x ) = G i ( x ) + ( x x i ) G i ( x ) G'(x)=G_i(x)+(x-x_i)G_i'(x)
x = x i x=x_i ,得:
G i ( x i ) = G ( x i ) G_i(x_i)=G'(x_i)
于是转化成了多点求值。
于是转化成:
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}
可以用分治 FFT 求得。
具体地,设 m i d = n + 1 2 mid=\lfloor\frac{n+1}2\rfloor L ( x ) = i = 1 m i d ( x x i ) L(x)=\prod_{i=1}^{mid}(x-x_i) R ( x ) = i = m i d + 1 n + 1 ( x x i ) R(x)=\prod_{i=mid+1}^{n+1}(x-x_i)
那么设 q i = y i G ( x i ) q_i=\frac{y_i}{G'(x_i)} ,则上式为:
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)
递归即可。
复杂度和多点求值一样是 O ( n log 2 n ) O(n\log^2n)
神仙 zzq 的博客:
orz zzq

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

一、引入

地球上的 OIer 都知道 Fibonacci 数列:
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)
n n 比较小时可以递推。
n n 规模比较大,如 1 0 9 10^9 甚至 1 0 18 10^{18} 时,可以利用矩阵乘法优化。
而假设由一个 k k 阶递推数列满足:
a n = i = 1 k f i × a k i a_n=\sum_{i=1}^kf_i\times a_{k-i}
a 0... k 1 a_{0...k-1} f 1... k f_{1...k} 是已知的。
这是一个 k k 阶的递推数列。
如果暴力转移,则复杂度 O ( n k ) O(nk)
如果使用矩阵乘法,则复杂度 O ( k 3 log n ) O(k^3\log n)
n 1 0 9 n\le 10^9 k 32000 k\le 32000 时,
就需要用到一个线性代数黑科技——常系数线性递推。

二、求法

假设转移矩阵为 A A ,初始列向量:
S t = [ a 0 a 1 a k 1 ] St=\begin{bmatrix}a_0\\a_1\\\vdots\\a_{k-1}\end{bmatrix}
则:
a n = A n × S t a_n=A^n\times St
假设我们构造了一个序列 c c 使得:
A n = i = 0 k 1 c i A i A^n=\sum_{i=0}^{k-1}c_iA^i
那么两边同乘 S t 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

三、构造序列 c c

考虑 c c 的生成函数 C ( A ) C(A) ,即 C C 是一个以矩阵为参数的多项式。
假设有
A n = F ( A ) G ( A ) + C ( A ) A^n=F(A)G(A)+C(A)
其中 G ( A ) G(A) 的次数为 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} ,那么就有:
C ( A ) = A n   m o d   G ( A ) C(A)=A^n\bmod G(A)
用快速幂+多项式取模就能做到 O ( k log k log n ) O(k\log k\log n) 的复杂度。
G ( A ) G(A) 为矩阵 A A 特征多项式

四、特征值和特征向量

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

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

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

定理:
i ( λ i I A ) = 0 \prod_i(\lambda_i I-A)=0
其中 λ i \lambda_i A A 的第 i i 个特征值。
为了证明这个定理,我们不妨先证明上式乘上任意特征向量等于 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)
然后我们能证明乘上 X j X_j 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
于是我们证明了 C-H 定理。

六、求特征多项式

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

七、模板

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

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#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 <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#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;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/82832467
今日推荐