文章目录
前言
下列的多项式操作基于FFT & NTT。
多项式操作
前置知识
NTT
void NTT(LL *A, LL Type)
{
for (LL i = 0; i < Limit; ++ i)
if (i < R[i])
swap(A[i], A[R[i]]);
for (LL i = 1; i < Limit; i <<= 1)
{
LL Root = QuickPow(Type == 1 ? G : InvG, (AllMod - 1) / (i << 1), AllMod);
for (LL j = 0; j < Limit; j += (i << 1))
{
LL Power = 1;
for (LL k = 0; k < i; ++ k, Power = Power * Root % AllMod)
{
LL x = A[j + k];
LL y = Power * A[i + k + j] % AllMod;
A[j + k] = (x + y) % AllMod;
A[i + j + k] = (x - y + AllMod) % AllMod;
}
}
}
if (Type == -1)
{
LL Inv = QuickPow(Limit, AllMod - 2, AllMod);
for (LL i = 0; i < Limit; ++ i)
A[i] = A[i] * Inv % AllMod;
}
}
导函数
定义
导函数,又称导数,又名微商,是微积分中的重要基础概念。当函数的自变量
x
x
x在一点
x
0
x0
x0上产生一个增量
Δ
x
Δx
Δx时,函数输出值的增量
Δ
y
Δy
Δy与自变量增量
Δ
x
Δx
Δx的比值在
Δ
x
Δx
Δx趋于0时的极限
a
a
a如果存在,
a
a
a即为在
x
0
x_0
x0处的导数,记作
f
′
(
x
0
)
f'(x_0)
f′(x0)或
d
f
(
x
0
)
/
d
x
df(x_0)/dx
df(x0)/dx
导数是函数的局部性质。一个函数在某一点的导数描述了这个函数在这一点附近的变化率。如果函数的自变量和取值都是实数的话,函数在某一点的导数就是该函数所代表的曲线在这一点上的切线斜率。导数的本质是通过极限的概念对函数进行局部的线性逼近。例如在运动学中,物体的位移对于时间的导数就是物体的瞬时速度。
详见\ 百度百科
公式表达
根据定义,可写出导函数的公式为
d
y
d
x
=
lim
Δ
x
→
0
Δ
x
Δ
y
\frac{dy}{dx} = \lim_{\Delta x\rightarrow 0} \frac{\Delta x}{\Delta y}
dxdy=Δx→0limΔyΔx
给出另一种简单一些的表达,令函数
f
(
x
0
)
f(x_0)
f(x0),它的导数为
f
′
(
x
0
)
f'(x_0)
f′(x0)
f
′
(
x
0
)
=
lim
Δ
x
→
0
f
(
x
+
Δ
x
)
−
f
(
x
)
Δ
x
f'(x_0)=\lim_{\Delta x\rightarrow 0}\frac{f(x+\Delta x)-f(x)}{\Delta x}
f′(x0)=Δx→0limΔxf(x+Δx)−f(x)
常用的求导公式
y
=
C
y=C
y=C,
y
′
=
0
y'=0
y′=0
y
=
x
k
y=x^k
y=xk,
y
′
=
k
x
k
−
1
y'=kx^{k-1}
y′=kxk−1
以我贫瘠的数学大概就用的了这两个了。其余的大家可自行百度。
切线
几何定义
P P P和 Q Q Q是曲线 C C C上邻近的两点, P P P是定点,当 Q Q Q点沿着曲线 C C C无限地接近 P P P点时,割线 P Q PQ PQ的极限位置 P T PT PT叫做曲线 C C C在点 P P P的切线, P P P点叫做切点;经过切点 P P P并且垂直于切线 P T PT PT的直线 P N PN PN叫做曲线 C C C在点 P P P的法线。
代数定义
在高等数学中,对于一个函数,如果函数某处有导数,那么此处的导数就是过此处的切线的斜率,该点和斜率所构成的直线就为该函数的一个切线。
详见 \ 百度百科
函数零点
函数零点就是当 f ( x ) = 0 f(x)=0 f(x)=0时对应的自变量 x x x的值,需要注意的是零点是一个数值,而不是一个点,是函数与X轴交点的横坐标。
详见 \ 百度百科
迭代相关
我们对多项式的优化方法常常运用迭代法。需要一些迭代的相关知识。
泰勒展开
不了解,也不详细讲。
简单介绍一下:
对于函数
f
(
x
)
f(x)
f(x),其在
x
=
x
0
x=x_0
x=x0处的泰勒展开式为:
f
(
x
)
=
f
(
x
0
)
0
!
+
f
′
(
x
0
)
1
!
(
x
−
x
0
)
+
f
′
′
(
x
0
)
2
!
(
x
−
x
0
)
2
+
.
.
.
+
f
(
n
)
′
(
x
0
)
n
!
(
x
−
x
0
)
n
f(x) = \frac{f(x_0)}{0!} + \frac{f'(x_0)}{1!}(x-x_0) + \frac{f''(x_0)}{2!}(x-x_0)^2 +...+ \frac{f^{(n)'}(x_0)}{n!}(x-x_0)^n
f(x)=0!f(x0)+1!f′(x0)(x−x0)+2!f′′(x0)(x−x0)2+...+n!f(n)′(x0)(x−x0)n
\ 百度百科
牛顿迭代
一种逼近零点的科技。
对于函数
f
(
x
)
f(x)
f(x),我们考虑从
x
=
x
i
x=x_i
x=xi为起点逼近其零点。
\ 百度百科
方法
作 ( x i , y i ) (x_i,y_i) (xi,yi)处的切线,设其交 x x x轴与 x i + 1 x_{i+1} xi+1,以 x i + 1 x_{i+1} xi+1为下一次的起点,重复该操作。过程中不断逼近零点。
式子
y
−
f
(
x
i
)
=
f
′
(
x
i
)
(
x
−
x
i
)
y-f(x_i)=f'(x_i)(x-x_i)
y−f(xi)=f′(xi)(x−xi)
令
y
=
0
y=0
y=0,得
x
i
+
1
=
x
i
−
f
(
x
i
)
f
′
(
x
i
)
x_{i+1}=x_i-\frac{f(x_i)}{f'(x_i)}
xi+1=xi−f′(xi)f(xi)
别急,分析一波
考虑直线方程
y
=
f
(
x
i
)
+
f
′
(
x
i
)
(
x
−
x
i
)
y=f(x_i)+f'(x_i)(x-x_i)
y=f(xi)+f′(xi)(x−xi),当你写出这个方程的泰勒展开时,你发现——
发现直线方程就是
f
(
x
)
f(x)
f(x)在
x
=
x
i
x=x_i
x=xi处的一阶泰勒展开。
定义
形式化地,我们可以这样描述牛顿迭代法:
用函数
f
(
x
)
f(x)
f(x)在
f
(
x
i
)
f(x_i)
f(xi)处的一阶展开方程作为下一次迭代的起点
x
i
xi
xi,这样的迭代法就是牛顿迭代法。
这个东西对于多项式有什么好处呢?
我们不难发现定义一个以多项式为自变量的函数
G
(
f
(
x
)
)
G(f(x))
G(f(x)),泰勒展开式仍成立(虽然我并没有发现)
我们首先将题目的形式转化为求
f
(
x
)
∣
G
(
f
(
x
)
)
≡
0
(
m
o
d
x
k
)
f(x)|G(f(x)) \equiv0 \ (mod \ x^k)
f(x)∣G(f(x))≡0 (mod xk)
一般步骤
已知
G
(
f
0
(
x
)
)
≡
0
(
m
o
d
x
k
)
G(f_0(x))\equiv\ 0(mod\ x^k)
G(f0(x))≡ 0(mod xk)
求
G
(
f
(
x
)
)
≡
0
(
m
o
d
x
2
k
)
G(f(x))\equiv\ 0(mod\ x^{2k})
G(f(x))≡ 0(mod x2k)
考虑从
G
(
f
(
x
)
)
G(f(x))
G(f(x))在
f
0
(
x
)
f_0(x)
f0(x)处的泰勒展开接近
f
(
x
)
f(x)
f(x)
得
G
(
f
(
x
)
)
≡
G
(
f
0
(
x
)
)
+
G
′
(
f
0
(
x
)
)
(
f
(
x
)
−
f
(
x
0
)
)
+
G
′
′
(
f
0
(
x
)
)
(
f
(
x
)
−
f
(
x
0
)
)
2
+
.
.
.
+
G
k
′
(
f
0
(
x
)
)
(
f
(
x
)
−
f
(
x
0
)
)
k
G(f(x))\equiv G(f_0(x))+G'(f_0(x))(f(x)-f(x_0))+G''(f_0(x))(f(x)-f(x_0))^2+...+G^{k'}(f_0(x))(f(x)-f(x_0))^k
G(f(x))≡G(f0(x))+G′(f0(x))(f(x)−f(x0))+G′′(f0(x))(f(x)−f(x0))2+...+Gk′(f0(x))(f(x)−f(x0))k
∵
f
(
x
)
≡
f
0
(
x
)
(
m
o
d
x
k
)
\because f(x) \equiv f_0(x)\ (mod \ x^k)
∵f(x)≡f0(x) (mod xk)
∴
f
(
x
)
−
f
0
(
x
)
≡
0
(
m
o
d
x
k
)
\therefore f(x) - f_0(x) \equiv 0\ (mod \ x^k)
∴f(x)−f0(x)≡0 (mod xk)
∴
(
f
(
x
)
−
f
0
(
x
)
)
2
≡
0
(
m
o
d
x
2
k
)
\therefore (f(x) - f_0(x))^2 \equiv 0\ (mod \ x^{2k})
∴(f(x)−f0(x))2≡0 (mod x2k)
结合上面推出的关于
G
(
f
(
x
)
)
G(f(x))
G(f(x))的式子,得:
G
(
f
(
x
)
)
≡
G
(
f
0
(
x
)
)
+
G
′
(
f
0
(
x
)
)
(
f
(
x
)
−
f
0
(
x
)
)
(
m
o
d
x
2
k
)
G(f(x)) \equiv G(f_0(x))+G'(f_0(x))(f(x)-f_0(x))\ (mod\ x^{2k})
G(f(x))≡G(f0(x))+G′(f0(x))(f(x)−f0(x)) (mod x2k)
所以,
f
(
x
)
f(x)
f(x)就为
f
0
(
x
)
f_0(x)
f0(x)牛顿迭代的结果
根据牛顿迭代的结果,我们可以得到:
f
(
x
)
≡
f
0
(
x
)
−
G
(
f
0
(
x
)
)
G
′
(
f
0
(
x
)
)
(
m
o
d
x
2
k
)
f(x)\equiv f_0(x)-\frac{G(f_0(x))}{G'(f_0(x))}\ (mod\ x^{2k})
f(x)≡f0(x)−G′(f0(x))G(f0(x)) (mod x2k)
基于这个结论,我们可以使用倍增的方法解决牛顿迭代。(然而我并不会)
多项式求逆
问题
已知
A
(
x
)
A(x)
A(x),求
A
(
x
)
A
−
1
(
x
)
≡
1
(
m
o
d
x
k
)
A(x)A^{-1}(x)\equiv1\ (mod\ x^k)
A(x)A−1(x)≡1 (mod xk)
//
(
m
o
d
n
k
)
(mod\ n^k)
(mod nk)即为舍去次数
≥
n
\geq n
≥n的项,只保留次数为
0
0
0到
n
−
1
{n-1}
n−1的项。
k = 1时,只有常数项,答案可以直接快速幂
假设已知
A
(
x
)
A(x)
A(x)在模
x
k
2
x^{\frac{k}{2}}
x2k下的逆元
B
0
(
x
)
B_0(x)
B0(x),满足:
A
(
x
)
B
0
(
x
)
≡
1
(
m
o
d
x
k
2
)
A(x)B_0(x)\equiv1\ (mod\ x^{\frac{k}{2}})
A(x)B0(x)≡1 (mod x2k)
求
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
x
k
)
A(x)B(x)\equiv1\ (mod\ x^k)
A(x)B(x)≡1 (mod xk)
两式相减,得:
A
(
x
)
B
(
x
)
−
A
(
x
)
B
0
(
x
)
≡
0
(
m
o
d
x
k
2
)
A(x)B(x)-A(x)B_0(x)\equiv0\ (mod\ x^{\frac{k}{2}})
A(x)B(x)−A(x)B0(x)≡0 (mod x2k)
A
(
x
)
(
B
(
x
)
−
B
0
(
x
)
)
≡
0
(
m
o
d
x
k
2
)
A(x)(B(x)-B_0(x))\equiv0\ (mod\ x^{\frac{k}{2}})
A(x)(B(x)−B0(x))≡0 (mod x2k)
B
(
x
)
−
B
0
(
x
)
≡
0
(
m
o
d
x
k
2
)
B(x)-B_0(x)\equiv0\ (mod\ x^{\frac{k}{2}})
B(x)−B0(x)≡0 (mod x2k)
(
B
(
x
)
−
B
0
(
x
)
)
2
≡
0
(
m
o
d
x
k
)
(B(x)-B_0(x))^2\equiv0\ (mod\ x^k)
(B(x)−B0(x))2≡0 (mod xk)
B
(
x
)
2
+
B
0
2
(
x
)
−
2
B
(
x
)
B
0
(
x
)
≡
0
(
m
o
d
x
k
)
B(x)^2+{B_0}^2(x)-2B(x)B_0(x)\equiv0\ (mod\ x^k)
B(x)2+B02(x)−2B(x)B0(x)≡0 (mod xk)
乘一个
A
(
x
)
A(x)
A(x),得
B
(
x
)
+
A
(
x
)
B
0
2
(
x
)
−
2
B
0
(
x
)
≡
0
(
m
o
d
x
k
)
B(x)+A(x){B_0}^2(x)-2B_0(x)\equiv0\ (mod\ x^k)
B(x)+A(x)B02(x)−2B0(x)≡0 (mod xk)
B
(
x
)
≡
2
B
0
(
x
)
−
A
(
x
)
B
0
2
(
m
o
d
x
k
)
B(x)\equiv2B_0(x)-A(x){B_0}^2\ (mod\ x^k)
B(x)≡2B0(x)−A(x)B02 (mod xk)
使用牛顿迭代,时间复杂度:
O
(
n
)
=
O
(
n
2
)
+
O
(
n
log
n
)
=
O
(
n
log
n
)
O(n)=O(\frac{n}{2})+O(n\log n)=O(n\log n)
O(n)=O(2n)+O(nlogn)=O(nlogn)
模板题
Code:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 1000005
#define LL long long
#define Mod 998244353
#define Int register int
using namespace std;
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
const LL G = 3;
LL a[MAXN], b[MAXN], c[MAXN], R[MAXN];
LL QuickPow(LL x,LL y)
{
LL Res = 1, temp = x;
while ( y )
{
if (y & 1)
Res = Res * temp % Mod;
temp = temp * temp % Mod;
y /= 2;
}
return Res;
}
void Swap(LL &x,LL &y)
{
LL temp = x;
x = y;
y = temp;
}
void NTT(LL *A,int Limit,int x)
{
for (Int i = 0; i < Limit; ++ i)
if (i < R[i])
Swap(A[i], A[R[i]]);
for (Int i = 1; i < Limit; i <<= 1)
{
LL Omi = QuickPow(G, (Mod - 1) / (i << 1));
for (Int j = 0; j < Limit; j += (i << 1))
{
LL Power = 1;
for (Int k = 0; k < i; ++ k, Power = Power * Omi % Mod)
{
LL x = A[j + k];
LL y = Power * A[i + j + k] % Mod;
A[j + k] = (x + y) % Mod;
A[j + k + i] = (x - y + Mod) % Mod;
}
}
}
if (x == 1)
return;
LL Inv = QuickPow(Limit, Mod - 2);
reverse(A + 1, A + Limit);
for (Int i = 0; i < Limit; ++ i)
A[i] = A[i] * Inv % Mod;
}
void Work(int Deg,LL *A,LL *B)
{
if (Deg == 1)
{
B[0] = QuickPow(A[0], Mod - 2);
return;
}
Work((Deg + 1) >> 1, A, B);
int l = 0, Limit = 1;
while (Limit < (Deg << 1))
Limit <<= 1 , ++ l;
for (Int i = 1; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) << (l - 1));
for (Int i = 0; i < Deg; ++ i)
c[i] = A[i];
for (Int i = Deg; i < Limit; ++ i)
c[i] = 0;
NTT(c, Limit, 1); NTT(B, Limit, 1);
for (Int i = 0; i < Limit; ++ i)
B[i] = (2 - c[i] * B[i] % Mod + Mod) % Mod * B[i] % Mod;
NTT(B, Limit, -1);
for (Int i = Deg; i < Limit; ++ i)
B[i] = 0;
}
int main()
{
int n;
n = read();
for (Int i = 0; i < n; ++ i)
a[i] = read();
Work(n, a, b);
for (Int i = 0; i < n; ++ i)
printf("%lld ", b[i]);
return 0;
}
多项式除法
思路
学习了NTT和多项式逆元,多项式除法就迎刃而解了。
多项式除法即,给定多项式F,G,求多项式Q,R,满足:
F
(
x
)
=
G
(
x
)
Q
(
x
)
+
R
(
x
)
F(x)=G(x)Q(x)+R(x)
F(x)=G(x)Q(x)+R(x)
将系数翻转得
x
n
F
(
1
x
)
=
x
m
G
(
1
x
)
x
n
−
m
Q
(
1
x
)
+
x
n
−
m
+
1
x
m
−
1
R
(
x
)
x^nF(\frac{1}{x})=x^mG(\frac{1}{x})x^{n-m}Q(\frac{1}{x})+x^{n-m+1}x^{m-1}R(x)
xnF(x1)=xmG(x1)xn−mQ(x1)+xn−m+1xm−1R(x)
设多项式
K
(
x
)
K(x)
K(x)的反转系数矩阵为
x
k
K
(
1
x
)
=
K
R
(
x
)
x^kK(\frac{1}{x})=K^R(x)
xkK(x1)=KR(x)
F
R
(
x
)
=
G
R
(
x
)
Q
R
(
x
)
+
x
n
−
m
+
1
R
R
(
x
)
F^R(x)=G^R(x)Q^R(x)+x^{n-m+1}R^R(x)
FR(x)=GR(x)QR(x)+xn−m+1RR(x)
F
R
(
x
)
≡
G
R
(
x
)
Q
R
(
x
)
(
m
o
d
x
n
−
m
+
1
)
F^R(x)\equiv G^R(x)Q^R(x)\ (mod\ x^{n-m+1})
FR(x)≡GR(x)QR(x) (mod xn−m+1)
F
R
(
x
)
G
R
(
x
)
−
1
≡
Q
R
(
x
)
(
m
o
d
x
n
−
m
+
1
)
F^R(x){G^R(x)}^{-1}\equiv Q^R(x)\ (mod\ x^{n-m+1})
FR(x)GR(x)−1≡QR(x) (mod xn−m+1)
将
G
R
G^R
GR代入,即得到
Q
R
Q^R
QR,
R
R
R^R
RR随之易得
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 1000005
#define LL long long
#define Int register int
using namespace std;
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
namespace Polynomial
{
const LL Mod = 998244353, YG = 3, YGi = 332748118;
static int R[MAXN];
int Limit, l;
LL Add(LL a, LL b)
{
LL Res = a + b;
return Res >= Mod ? Res - Mod : Res;
}
LL QuickPow(LL x, LL y)
{
LL Res = 1, temp = x;
while ( y )
{
if (y & 1)
Res = Res * temp % Mod;
temp = temp * temp % Mod;
y /= 2;
}
return (Res + Mod) % Mod;
}
void InitR()
{
for(Int i = 1; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) << (l - 1));
}
void Swap(LL &x,LL &y)
{
LL temp = x;
x = y;
y = temp;
}
void NTT(LL *A, int Type)
{
for (Int i = 0; i < Limit; ++ i)
if (i < R[i])
Swap(A[i], A[R[i]]);
for (Int Mid = 1; Mid < Limit; Mid <<= 1)
{
LL Omi = QuickPow(Type == 1 ? YG : YGi, (Mod - 1) / (Mid << 1));
for (Int j = 0; j < Limit; j += (Mid << 1))
{
LL Power = 1;
for (Int k = 0; k < Mid; ++ k, Power = (Power * Omi) % Mod)
{
int x = A[j + k];
int y = Power * A[j + k + Mid] % Mod;
A[j + k] = (x + y) % Mod;
A[j + k + Mid] = (x - y + Mod) % Mod;
}
}
}
if (Type == 1)
return;
int Inv = QuickPow(Limit, Mod - 2);
for (Int i = 0; i < Limit; ++ i)
A[i] = A[i] * Inv % Mod;
}
static LL x[MAXN], y[MAXN];
void Poly_QuickPow(LL *a, LL *b)
{
memset(x, 0, sizeof x);
memset(y, 0, sizeof y);
for (Int i = 0; i < (Limit >> 1); ++ i)
x[i] = a[i], y[i] = b[i];
NTT(x, 1); NTT(y, 1);
for (Int i = 0; i < Limit; ++ i)
x[i] = x[i] * y[i] % Mod;
NTT(x, -1);
for (Int i = 0; i < Limit; ++ i)
a[i] = x[i];
}
static LL c[2][MAXN];
void GetInv(LL *a, int n)
{
int p = 0;
memset(c, 0, sizeof c);
c[0][0] = QuickPow(a[0], Mod - 2);
Limit = 2, l = 1;
while (Limit <= (n << 1))
{
Limit <<= 1, l ++;
InitR();
p ^= 1;
memset(c[p], 0, sizeof c[p]);
for (Int i = 0; i <= Limit; ++ i)
c[p][i] = Add(c[p ^ 1][i], c[p ^ 1][i]);
Poly_QuickPow(c[p ^ 1], c[p ^ 1]);
Poly_QuickPow(c[p ^ 1], a);
for (Int i = 0; i <= Limit; ++ i)
c[p][i] = Add(c[p][i], Mod - c[p ^ 1][i]);
}
for (Int i = 0; i < Limit; ++ i)
a[i] = c[p][i];
}
}
using namespace Polynomial;
int n, m;
LL F[MAXN], G[MAXN], Q[MAXN], R[MAXN], Gr[MAXN];
int main()
{
n = read(); m = read();
for (Int i = 0; i <= n; ++ i)
F[i] = read(), Q[n - i] = F[i];
for (Int i = 0; i <= m; ++ i)
G[i] = read(), Gr[m - i] = G[i];
for (Int i = n - m + 2; i <= m; ++ i)
Gr[i] = 0;
GetInv(Gr, n - m + 1);
Poly_QuickPow(Q, Gr);
reverse(Q, Q + n - m + 1);
for (Int i = n - m + 1; i <= n; ++ i)
Q[i] = 0;
for (Int i = 0; i <= n - m; ++ i)
printf("%lld ", Q[i]);
printf("\n");
Limit = 1, l = 0;
while (Limit <= (n << 2))
Limit <<= 1, l ++;
InitR();
Poly_QuickPow(Q, G);
for (Int i = 0; i < m; ++ i)
printf("%lld ", Add(F[i], Mod - Q[i]));
return 0;
}
多项式对数函数(多项式ln)
自然对数
自然对数是以常数
e
e
e为底数的对数, 记做
ln
(
n
)
(
n
>
0
)
\ln(n) \ (n > 0)
ln(n) (n>0)。
常数
e
e
e的含义是单位时间内,持续的翻倍增长所能达到的极限值。
自然对数的底
e
e
e是由一个重要极限给出的。定义:
e
=
lim
n
→
∞
(
1
+
1
n
)
n
e=\lim_{n\rightarrow\infty}{(1+\frac{1}{n})}^n
e=n→∞lim(1+n1)n
e
e
e是一个无限不循环小数,
≈
2.718281828459
…
\approx2.718281828459…
≈2.718281828459…
\ 百度百科
积分
积分是微积分学与数学分析里的一个核心概念。通常分为定积分和不定积分两种。
直观地说,对于一个给定的正实值函数,在一个实数区间上的定积分可以理解为在坐标平面上,由曲线、直线以及轴围成的曲边梯形的面积值(一种确定的实数值)。(并看不懂)
\ 百度百科
积分是微分的逆运算,即知道了函数的导函数,反求原函数。
然而看不懂也没有办法,只能记住——
积分公式
定积分:
∫
a
b
f
(
x
)
d
x
\int_{a}^{b}f(x)dx
∫abf(x)dx
不定积分:
∫
f
(
x
)
d
x
=
F
(
x
)
+
C
\int f(x)dx=F(x)+C
∫f(x)dx=F(x)+C
不定积分
设 F ( x ) F(x) F(x)是函数 f ( x ) f(x) f(x)的一个原函数,我们把函数 f ( x ) f(x) f(x)的所有原函数 F ( x ) + C F(x)+C F(x)+C(C为任意常数)叫做函数 f ( x ) f(x) f(x)的不定积分,即 ∫ f ( x ) d x = F ( x ) + C \int f(x)dx=F(x)+C ∫f(x)dx=F(x)+C。其中 ∫ ∫ ∫叫做积分号, f ( x ) f(x) f(x)叫做被积函数, x x x叫做积分变量, f ( x ) d x f(x)dx f(x)dx叫做被积式,C叫做积分常数,求已知函数不定积分的过程叫做对这个函数进行积分。
定积分
对于一个给定的实函数
f
(
x
)
f(x)
f(x),在区间
[
a
,
b
]
[a,b]
[a,b]上的定积分记为:
∫
a
b
f
(
x
)
d
x
\int_{a}^{b}f(x)dx
∫abf(x)dx
若
f
(
x
)
f(x)
f(x)在
[
a
,
b
]
[a,b]
[a,b]上恒为正,可以将定积分理解为在
O
x
y
Oxy
Oxy坐标平面上,由曲线
(
x
,
f
(
x
)
)
(x,f(x))
(x,f(x))、直线
x
=
a
x=a
x=a、
x
=
b
x=b
x=b以及
x
x
x轴围成的面积值(一种确定的实数值)。
然而还是不懂,先打板子吧(汗…)
思路
数学太差,求这个对数函数有点懵,只能理解理解背板了。
前置公式:
ln
F
(
x
)
=
∫
F
′
(
x
)
F
−
1
(
x
)
d
x
\ln F(x)=\int F'(x)F^{-1}(x)dx
lnF(x)=∫F′(x)F−1(x)dx
由指数函数的求导公式:
x
α
′
=
α
x
α
−
1
x^{\alpha'}=\alpha x^{\alpha - 1}
xα′=αxα−1
∫
x
α
d
x
=
x
α
+
1
α
+
1
\int x^{\alpha}dx=\frac{x^{\alpha + 1}}{\alpha + 1}
∫xαdx=α+1xα+1
令
F
(
x
)
=
∑
i
=
1
n
a
i
x
i
F(x)=\sum_{i=1}^{n}a_ix^i
F(x)=i=1∑naixi
n
=
D
e
g
(
F
)
n=Deg(F)
n=Deg(F)
得到
F
′
(
x
)
=
∑
i
=
1
n
i
∗
a
i
x
i
−
1
F'(x)=\sum_{i=1}^{n}i*a_ix^{i-1}
F′(x)=i=1∑ni∗aixi−1
∫
F
(
x
)
d
x
=
∑
i
=
1
n
a
i
x
i
+
1
i
+
1
\int F(x)dx=\sum_{i=1}^{n}a_i\frac{x^{i + 1}}{i + 1}
∫F(x)dx=i=1∑naii+1xi+1
- 求导函数可以参考程序 D e r i v a t i v e Derivative Derivative段
- 求不定积分可以参考程序 I n t e r Inter Inter段
Code:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 1000005
#define LL long long
#define Int register int
using namespace std;
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
namespace Polynomial
{
const LL Mod = 998244353, YG = 3, YGi = 332748118;
static int R[MAXN];
int Limit, l;
void Swap(LL &x,LL &y)
{
LL temp = x;
x = y;
y = temp;
}
LL Add(LL a, LL b)
{
LL Res = a + b;
return Res >= Mod ? Res - Mod : Res;
}
LL QuickPow(LL x, LL y)
{
LL Res = 1, temp = x;
while ( y )
{
if (y & 1)
Res = Res * temp % Mod;
temp = temp * temp % Mod;
y /= 2;
}
return (Res + Mod) % Mod;
}
void InitR()
{
for(Int i = 1; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) << (l - 1));
}
void NTT(LL *A, int Type)
{
for (Int i = 0; i < Limit; ++ i)
if (i < R[i])
Swap(A[i], A[R[i]]);
for (Int Mid = 1; Mid < Limit; Mid <<= 1)
{
LL Omi = QuickPow(Type == 1 ? YG : YGi, (Mod - 1) / (Mid << 1));
for (Int j = 0; j < Limit; j += (Mid << 1))
{
LL Power = 1;
for (Int k = 0; k < Mid; ++ k, Power = (Power * Omi) % Mod)
{
int x = A[j + k];
int y = Power * A[j + k + Mid] % Mod;
A[j + k] = (x + y) % Mod;
A[j + k + Mid] = (x - y + Mod) % Mod;
}
}
}
if (Type == 1)
return;
int Inv = QuickPow(Limit, Mod - 2);
for (Int i = 0; i < Limit; ++ i)
A[i] = A[i] * Inv % Mod;
}
static LL x[MAXN], y[MAXN];
void Poly_QuickPow(LL *a, LL *b)
{
memset(x, 0, sizeof x);
memset(y, 0, sizeof y);
for (Int i = 0; i < (Limit >> 1); ++ i)
x[i] = a[i], y[i] = b[i];
NTT(x, 1); NTT(y, 1);
for (Int i = 0; i < Limit; ++ i)
x[i] = x[i] * y[i] % Mod;
NTT(x, -1);
for (Int i = 0; i < Limit; ++ i)
a[i] = x[i];
}
static LL c[2][MAXN];
void GetInv(LL *a, int n)
{
int p = 0;
memset(c, 0, sizeof c);
c[0][0] = QuickPow(a[0], Mod - 2);
Limit = 2, l = 1;
while (Limit <= (n << 1))
{
Limit <<= 1, l ++;
InitR();
p ^= 1;
memset(c[p], 0, sizeof c[p]);
for (Int i = 0; i <= Limit; ++ i)
c[p][i] = Add(c[p ^ 1][i], c[p ^ 1][i]);
Poly_QuickPow(c[p ^ 1], c[p ^ 1]);
Poly_QuickPow(c[p ^ 1], a);
for (Int i = 0; i <= Limit; ++ i)
c[p][i] = Add(c[p][i], Mod - c[p ^ 1][i]);
}
for (Int i = 0; i < Limit; ++ i)
a[i] = c[p][i];
}
void Derivative(LL *a, int n)
{
for (Int i = 1; i <= n; ++ i)
a[i - 1] = a[i] * i % Mod;
a[n] = 0;
}
void Inter(LL *a, int n)
{
for (Int i = n; i >= 1; i--)
a[i] = a[i - 1] * QuickPow(i, Mod - 2) % Mod;
a[0] = 0;
}
LL b[MAXN];
void ln(LL *a, int n)
{
memcpy(b, a, sizeof b);
GetInv(b, n);
Derivative(a, n);
while (Limit < (n << 2))
Limit <<= 1, l ++;
InitR();
Poly_QuickPow(a, b);
Inter(a, n);
for (Int i = n + 1; i <= Limit; ++ i)
a[i] = 0;
}
}
using namespace Polynomial;
int n;
LL F[MAXN];
int main()
{
n = read();
for(int i = 0; i < n; i++)
F[i] = read();
Limit = 2, l = 1;
while (Limit < (n << 1))
Limit <<= 1, l ++;
ln(F, n);
for (Int i = 0; i < n; ++ i)
printf("%d ", F[i]);
return 0;
}
多项式指数函数(多项式exp)
指数函数
指数函数是重要的基本初等函数之一。一般地,
y
=
a
x
y=a^x
y=ax函数(
a
a
a为常数且
a
>
0
a>0
a>0,
a
≠
1
a≠1
a=1)叫做指数函数。 在指数函数的定义表达式中,在
a
x
a^x
ax前的系数必须是数
1
1
1,自变量
x
x
x必须在指数的位置上,且不能是
x
x
x的其他表达式,否则,就不是指数函数。
应用到值
e
e
e上的指数函数写为
exp
(
x
)
\exp(x)
exp(x)。还可以等价的写为
e
x
e^x
ex
\ 百度百科
题目
思路
求
B
(
x
)
≡
e
A
(
x
)
(
m
o
d
x
n
)
B(x)\equiv e^{A(x)}\ (mod\ x^n)
B(x)≡eA(x) (mod xn)
等价于
ln
(
B
(
x
)
)
−
A
(
x
)
≡
0
(
m
o
d
x
n
)
\ln(B(x))- A(x)\equiv 0\ (mod\ x^n)
ln(B(x))−A(x)≡0 (mod xn)
令
G
(
B
(
x
)
)
≡
ln
(
B
(
x
)
)
−
A
(
x
)
(
m
o
d
x
n
)
G(B(x))\equiv \ln(B(x))- A(x)\ (mod\ x^n)
G(B(x))≡ln(B(x))−A(x) (mod xn)
这是一个关于多项式
B
(
x
)
B(x)
B(x)的函数,对它求导时,把与它平齐的多项式
A
(
x
)
A(x)
A(x)看做常数。
G
′
(
B
(
x
)
)
=
B
−
1
(
x
)
G'(B(x))=B^{-1}(x)
G′(B(x))=B−1(x)
套入牛顿迭代:
B
(
x
)
≡
B
0
(
x
)
(
1
−
ln
B
0
(
x
)
+
A
(
x
)
)
(
m
o
d
x
n
)
B(x)\equiv B_0(x)(1-\ln B_0(x)+A(x))\ (mod\ x^n)
B(x)≡B0(x)(1−lnB0(x)+A(x)) (mod xn)
Code:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 1000005
#define LL long long
#define Int register int
using namespace std;
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
void Write(LL x)
{
if (x > 9)
Write(x / 10);
putchar(x % 10 ^ 48);
}
namespace Polynomial
{
const int Mod = 998244353, YG = 3, YGi = 332748118;
static int R[MAXN];
int Limit, l;
inline void Swap(int &x,int &y)
{
int temp = x;
x = y;
y = temp;
}
inline int Add(int a,int b)
{
LL Res = a + b;
return Res >= Mod ? Res - Mod : Res;
}
inline int QuickPow(int x,int y)
{
int Res = 1, temp = x;
while ( y )
{
if (y & 1)
Res = 1ll * Res * temp % Mod;
temp = 1ll * temp * temp % Mod;
y /= 2;
}
return (0ll + Res + Mod) % Mod;
}
inline void InitR()
{
for (Int i = 1; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) << (l - 1));
}
void NTT(int *A, int Type)
{
for (Int i = 0; i < Limit; ++ i)
if (i < R[i])
Swap(A[i], A[R[i]]);
for (Int Mid = 1; Mid < Limit; Mid <<= 1)
{
int Omi = QuickPow(Type == 1 ? YG : YGi, (Mod - 1) / (Mid << 1));
for (Int j = 0; j < Limit; j += (Mid << 1))
{
int Power = 1;
for (Int k = 0; k < Mid; ++ k, Power = (1ll * Power * Omi) % Mod)
{
int x = A[j + k];
int y = 1ll * Power * A[j + k + Mid] % Mod;
A[j + k] = (0ll + x + y) % Mod;
A[j + k + Mid] = (0ll + x - y + Mod) % Mod;
}
}
}
if (Type == 1)
return;
int Inv = QuickPow(Limit, Mod - 2);
for (Int i = 0; i < Limit; ++ i)
A[i] = 1ll * A[i] * Inv % Mod;
}
static int x[MAXN], y[MAXN];
inline void Poly_QuickPow(int *a,int *b)
{
memset(x, 0, sizeof x);
memset(y, 0, sizeof y);
for (Int i = 0; i < (Limit >> 1); ++ i)
x[i] = a[i], y[i] = b[i];
NTT(x, 1); NTT(y, 1);
for (Int i = 0; i < Limit; ++ i)
x[i] = 1ll * x[i] * y[i] % Mod;
NTT(x, -1);
for (Int i = 0; i < Limit; ++ i)
a[i] = x[i];
}
static int c[2][MAXN];
void GetInv(int *a, int n)
{
int p = 0;
memset(c, 0, sizeof c);
c[0][0] = QuickPow(a[0], Mod - 2);
Limit = 2, l = 1;
while (Limit < (n << 1))
{
Limit <<= 1, l ++;
InitR();
p ^= 1;
memset(c[p], 0, sizeof c[p]);
for (Int i = 0; i <= Limit; ++ i)
c[p][i] = Add(c[p ^ 1][i], c[p ^ 1][i]);
Poly_QuickPow(c[p ^ 1], c[p ^ 1]);
Poly_QuickPow(c[p ^ 1], a);
for (Int i = 0; i <= Limit; ++ i)
c[p][i] = Add(c[p][i], Mod - c[p ^ 1][i]);
}
for (Int i = 0; i < Limit; ++ i)
a[i] = c[p][i];
}
void Derivative(int *a, int n)
{
for (Int i = 1; i <= n; ++ i)
a[i - 1] = 1ll * a[i] * i % Mod;
a[n] = 0;
}
void Inter(int *a, int n)
{
for (Int i = n; i >= 1; -- i)
a[i] = 1ll * a[i - 1] * QuickPow(i, Mod - 2) % Mod;
a[0] = 0;
}
static int b[MAXN], T[MAXN], K[MAXN];
void ln(int *a, int n)
{
memcpy(b, a, sizeof b);
GetInv(b, n);
Derivative(a, n);
while (Limit < (n << 2))
Limit <<= 1, l ++;
InitR();
Poly_QuickPow(a, b);
Inter(a, n);
for (Int i = n + 1; i <= Limit; ++ i)
a[i] = 0;
}
void exp(int *a, int n)
{
static int z, d;
z = Limit = 2, d = l = 1;
memset(K, 0, sizeof K);
K[0] = 1;
while (z < (n << 1))
{
z <<= 1, d ++;
for (Int i = 0; i < (z >> 1); ++ i)
T[i] = K[i];
ln(T, (z >> 1) - 1);
for (Int i = 0; i < (z >> 1); ++ i)
T[i] = Add(a[i] + (i == 0), Mod - T[i]);
Limit = z, l = d;
InitR();
Poly_QuickPow(K, T);
for (Int i = z; i <= (z << 1); ++ i)
K[i] = T[i] = 0;
}
for (Int i = 0; i <= n; ++ i)
a[i] = K[i];
}
}
using namespace Polynomial;
int n, F[MAXN];
int main()
{
n = read();
for (Int i = 0; i < n; ++ i)
F[i] = read();
exp(F, n);
for (Int i = 0; i < n; ++ i)
printf("%d " ,F[i]);
return 0;
}
PS:
一开始全开long long T飞了,还是得改 int
多项式开根
已知
A
(
x
)
A(x)
A(x),求
B
(
x
)
B(x)
B(x)使得
B
(
x
)
2
≡
A
(
x
)
(
m
o
d
x
n
)
B(x)^2\equiv A(x)\ (mod\ x^n)
B(x)2≡A(x) (mod xn)
法一:
易得
ln
B
(
x
)
≡
ln
A
(
x
)
2
(
m
o
d
x
n
)
\ln B(x)\equiv \frac{\ln A(x)}{2}\ (mod\ x^n)
lnB(x)≡2lnA(x) (mod xn)
得
B
(
x
)
≡
exp
(
ln
A
(
x
)
2
)
(
m
o
d
x
n
)
B(x)\equiv \exp(\frac{\ln A(x)}{2})\ (mod\ x^n)
B(x)≡exp(2lnA(x)) (mod xn)
套板子即可。然而这样常数比较大。
法二:
易得
B
(
x
)
2
−
A
(
x
)
≡
0
(
m
o
d
x
n
)
B(x)^2-A(x)\equiv0\ (mod\ x^n)
B(x)2−A(x)≡0 (mod xn)
设
G
(
B
(
x
)
)
≡
B
(
x
)
2
−
A
(
x
)
(
m
o
d
x
n
)
G(B(x))\equiv B(x)^2-A(x)\ (mod\ x^n)
G(B(x))≡B(x)2−A(x) (mod xn)
G
(
B
(
x
)
)
G(B(x))
G(B(x))与
A
(
x
)
A(x)
A(x)为齐次式,将
A
(
x
)
A(x)
A(x)看作常数,根据求导公式,得
G
′
(
B
(
x
)
)
≡
2
B
(
x
)
(
m
o
d
x
n
)
G'(B(x))\equiv 2B(x)\ (mod\ x^n)
G′(B(x))≡2B(x) (mod xn)
于是进行牛顿迭代:
B
(
x
)
≡
B
0
(
x
)
−
G
(
B
0
(
x
)
)
G
′
(
B
0
(
x
)
)
(
m
o
d
x
n
)
B(x)\equiv B_0(x)-\frac{G(B_0(x))}{G'(B_0(x))}\ (mod\ x^n)
B(x)≡B0(x)−G′(B0(x))G(B0(x)) (mod xn)
化简
B
(
x
)
≡
2
B
0
(
x
)
2
−
B
0
(
x
)
2
+
A
(
x
)
2
B
0
(
x
)
(
m
o
d
x
n
)
B(x)\equiv \frac{2B_0(x)^2-B_0(x)^2+A(x)}{2B_0(x)}\ (mod\ x^n)
B(x)≡2B0(x)2B0(x)2−B0(x)2+A(x) (mod xn)
B
(
x
)
≡
B
0
(
x
)
2
+
A
(
x
)
2
B
0
(
x
)
(
m
o
d
x
n
)
B(x)\equiv \frac{B_0(x)^2+A(x)}{2B_0(x)}\ (mod\ x^n)
B(x)≡2B0(x)B0(x)2+A(x) (mod xn)
Code:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 1000005
#define LL long long
#define Int register int
using namespace std;
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
void Write(LL x)
{
if (x > 9)
Write(x / 10);
putchar(x % 10 ^ 48);
}
namespace Polynomial
{
const int Inv2 = 499122177, Mod = 998244353, YG = 3, YGi = 332748118;
static int R[MAXN], F[MAXN], G[MAXN], A[MAXN], B[MAXN], C[MAXN], D[MAXN];
inline int Add(int a,int b)
{
LL Res = a + b;
return Res >= Mod ? Res - Mod : Res;
}
inline int QuickPow(int x,int y)
{
int Res = 1, temp = x;
while ( y )
{
if (y & 1)
Res = 1ll * Res * temp % Mod;
temp = 1ll * temp * temp % Mod;
y /= 2;
}
return (0ll + Res + Mod) % Mod;
}
inline void Swap(int &x,int &y)
{
int temp = x;
x = y;
y = temp;
}
void NTT(int *A,int Limit,int Type)
{
for (Int i = 0; i < Limit; ++ i)
if (i < R[i])
Swap(A[i], A[R[i]]);
for (Int Mid = 1; Mid < Limit; Mid <<= 1)
{
int Omi = QuickPow(Type == 1 ? YG : YGi, (Mod - 1) / (Mid << 1));
for (Int j = 0; j < Limit; j += (Mid << 1))
{
int Power = 1;
for (Int k = 0; k < Mid; ++ k, Power = (1ll * Power * Omi) % Mod)
{
int x = A[j + k];
int y = 1ll * Power * A[j + k + Mid] % Mod;
A[j + k] = (0ll + x + y) % Mod;
A[j + k + Mid] = (0ll + x - y + Mod) % Mod;
}
}
}
if (Type == 1)
return;
int Inv = QuickPow(Limit, Mod - 2);
for (Int i = 0; i < Limit; ++ i)
A[i] = 1ll * A[i] * Inv % Mod;
}
void GetInv(int *a,int *b,int n)
{
b[0] = QuickPow(a[0], Mod - 2);
int Limitl, Limit;
for (Limitl = 1; Limitl < (n << 1); Limitl <<= 1)
{
Limit = Limitl << 1;
for (Int i = 0; i < Limitl; ++ i)
A[i] = a[i], B[i] = b[i];
for (Int i = 0; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) ? Limitl : 0);
NTT(A, Limit, 1); NTT(B, Limit, 1);
for (Int i = 0; i < Limit; ++ i)
b[i] = ((2ll - 1ll * A[i] * B[i] % Mod) * B[i] % Mod + Mod) % Mod;
NTT(b, Limit, -1);
for (Int i = Limitl; i < Limit; ++ i)
b[i] = 0;
}
for (Int i = 0; i < Limitl; ++ i)
A[i] = B[i] = 0;
for (Int i = n; i < Limitl; ++ i)
b[i] = 0;
}
void Sqrt(int *a,int *b,int n)
{
b[0] = 1;
int *A = C, *B = D, Limitl, Limit;
for (Limitl = 1; Limitl < (n << 1); Limitl <<= 1)
{
Limit = Limitl << 1;
for (Int i = 0; i < Limitl; ++ i)
A[i] = a[i];
GetInv(b, B, Limitl);
for (Int i = 0; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) ? Limitl : 0);
NTT(A, Limit, 1); NTT(B, Limit, 1);
for (Int i = 0; i < Limit; ++ i)
A[i] = 1ll * A[i] * B[i] % Mod;
NTT(A, Limit, -1);
for (Int i = 0; i < Limitl; ++ i)
b[i] = 1ll * (b[i] + A[i]) % Mod * Inv2 % Mod;
for (Int i = Limitl; i < Limit; ++ i)
b[i] = 0;
}
for (Int i = 0; i < Limitl; ++ i)
A[i] = B[i] = 0;
for (Int i = n; i < Limitl; ++ i)
b[i] = 0;
}
}
using namespace Polynomial;
int main()
{
int n = read();
for (Int i = 0; i < n; ++ i)
F[i] = read();
Sqrt(F, G, n);
for (Int i = 0; i < n; ++ i)
printf("%d ", G[i]);
return 0;
}
多项式快速幂
题目
思路
求
B
(
x
)
≡
A
(
x
)
k
(
m
o
d
x
n
)
B(x)\equiv A(x)^k\ (mod\ x^n)
B(x)≡A(x)k (mod xn)
易得
ln
B
(
x
)
≡
ln
A
(
x
)
k
(
m
o
d
x
n
)
\ln B(x)\equiv \ln {A(x)}^k\ (mod\ x^n)
lnB(x)≡lnA(x)k (mod xn)
ln
B
(
x
)
≡
k
ln
A
(
x
)
(
m
o
d
x
n
)
\ln B(x)\equiv k\ln {A(x)}\ (mod\ x^n)
lnB(x)≡klnA(x) (mod xn)
转换得
B
(
x
)
≡
e
k
ln
A
(
x
)
(
m
o
d
x
n
)
B(x)\equiv e^{k\ln {A(x)}}\ (mod\ x^n)
B(x)≡eklnA(x) (mod xn)
B
(
x
)
≡
exp
(
k
ln
A
(
x
)
)
(
m
o
d
x
n
)
B(x)\equiv \exp({k\ln {A(x)}})\ (mod\ x^n)
B(x)≡exp(klnA(x)) (mod xn)
套板子即可
Code:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 1000005
#define LL long long
#define Int register int
const int Mod = 998244353, YG = 3, YGi = 332748118;
using namespace std;
int n, k, a[MAXN], A[MAXN], G[MAXN];
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
inline int Modread()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (10ll * x + (s ^ 48)) % Mod;
s = getchar();
}
return x * f;
}
inline int QuickPow(int x,int y)
{
int Res = 1, temp = x;
while ( y )
{
if (y & 1)
Res = 1ll * Res * temp % Mod;
temp = 1ll * temp * temp % Mod;
y /= 2;
}
return Res;
}
namespace Polynomial
{
int R[MAXN];
inline void Swap(int &x,int &y)
{
int temp = x;
x = y;
y = temp;
}
void NTT(int *A, int n, int Type)
{
for (Int i = 0; i < n; ++ i)
if (i < R[i])
Swap(A[i], A[R[i]]);
for (Int Mid = 1; Mid < n; Mid <<= 1)
{
int Ome = QuickPow(Type == 1 ? YG : YGi, (Mod - 1) / (Mid << 1));
for (Int j = 0; j < n; j += (Mid << 1))
{
int Power = 1;
for (Int k = 0; k < Mid; ++ k, Power = 1ll * Power * Ome % Mod)
{
int x = A[j + k];
int y = 1ll * Power * A[j + k + Mid] % Mod;
A[j + k] = (0ll + x + y) % Mod;
A[j + k + Mid] = (0ll + x - y + Mod) % Mod;
}
}
}
if (Type == 1)
return;
int Inv = QuickPow(n, Mod - 2);
for (Int i = 0; i < n; ++ i)
A[i] = 1ll * A[i] * Inv % Mod;
}
void Inv(int *a, int *b, int n)
{
static int B[MAXN], A[MAXN];
b[0] = QuickPow(a[0], Mod - 2);
int Limitl, Limit;
for (Limitl = 1; Limitl < (n << 1); Limitl <<= 1)
{
Limit = Limitl << 1;
for (Int i = 0; i < Limitl; ++ i)
A[i] = a[i], B[i] = b[i];
for (Int i = 0; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) ? Limitl : 0);
NTT(A, Limit, 1);
NTT(B, Limit, 1);
for (Int i = 0; i < Limit; ++ i)
b[i] = ((2ll - 1ll * A[i] * B[i] % Mod) * B[i] % Mod + Mod) % Mod;
NTT(b, Limit, -1);
for (Int i = Limitl; i < Limit; ++ i)
b[i] = 0;
}
for (Int i = 0; i < Limitl; ++ i)
A[i] = B[i] = 0;
for (Int i = n; i < Limitl; ++ i)
b[i] = 0;
}
inline void Derivative(int *a,int *b,int n)
{
b[n - 1] = 0;
for (Int i = 1; i < n; ++ i)
b[i - 1] = 1ll * a[i] * i % Mod;
}
inline void Inter(int *a,int *b,int n)
{
*b = 0;
for (Int i = n - 1; i >= 0; -- i)
b[i + 1] = a[i] * 1ll * QuickPow(i + 1, Mod - 2) % Mod;
}
inline void ln(int *a, int *b, int n)
{
static int F[MAXN];
Derivative(a, F, n);
Inv(a, b, n);
int Limit = 1;
while (Limit < (n << 1))
Limit <<= 1;
for (Int i = 1; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) ? (Limit >> 1) : 0);
for (Int i = n; i < Limit; ++ i)
b[i] = F[i] = 0;
NTT(F, Limit, 1);
NTT(b, Limit, 1);
for (Int i = 0; i < Limit; ++ i)
F[i] = 1ll * b[i] * F[i] % Mod;
NTT(F, Limit, 0);
Inter(F, b, n);
for (Int i = n; i < Limit; ++ i)
b[i] = 0;
}
void exp(int *a, int *F, int n)
{
if(n == 1)
*F = 1;
else
{
exp(a, F, n + 1 >> 1);
static int F0[MAXN], A[MAXN];
for (Int i = 0; i <= (n << 1); ++ i)
F0[i] = 0, A[i] = a[i];
ln(F, F0, n);
int Limit = 1;
while (Limit < (n << 1))
Limit <<= 1;
for (Int i = 1; i < Limit; ++ i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) ? (Limit >> 1) : 0);
for (Int i = n; i < Limit; ++ i)
A[i] = 0;
NTT(A, Limit, 1);
NTT(F0, Limit, 1);
NTT(F, Limit, 1);
for (Int i = 0; i < Limit; ++ i)
F[i] = F[i] * (A[i] + 1ll - F0[i] + Mod) % Mod;
NTT(F, Limit, 0);
for (Int i = n; i < Limit; ++ i)
F[i] = 0;
}
}
}
using namespace Polynomial;
int main()
{
n = read();
k = Modread();
for (Int i = 0; i < n; ++ i)
a[i] = read();
ln(a, A, n);
for (Int i = 0; i < n; ++ i)
A[i] = 1ll * A[i] * k % Mod;
for (Int i = 0; i < n; ++i)
a[i] = 0;
exp(A, a, n);
for (Int i = 0; i < n; ++ i)
printf("%d ", a[i]);
return 0;
}
后记
啊,终于写完了,写多项式真是一种折磨。
这些代码要是背的话是很难的,应该先理解思路,然后做一些题练练板子的使用,自然而然就熟练了。
完结撒花~~~