多项式的几个板子

多项式的几个板子,代码也放在一起了.

Luogu P4238 多项式求逆

给定一个 $n$ 项的多项式 $A$ ,求多项式 $B$ ,使得 $A\cdot B\equiv 1 (\mbox{mod}\ x^n)$ .

首先还是用 $0$ 将 $n$ 补成 $2$ 的幂次,然后递归求解.

若 $n=1$ ,那么只需要让 $B=A_0^{-1}$ .

若 $n>1$ ,则先求解 $B’$ ,使得 $A\cdot B’\equiv 1(\mbox{mod}\ x^{\frac n 2})$ .

因为 $A\cdot B\equiv 1(\mbox{mod}\ x^n)$ ,所以有 $B-B’\equiv 0(\mbox{mod}\ x^{\frac n 2})$ .

即 $B-B’=C\cdot x^{\frac n 2}$ ,所以 $B^2-2\cdot B\cdot B’+B’^2\equiv 0(\mbox{mod}\ x^n)$ .

两边同乘 $A$ ,得到 $B-2B’+A\cdot B’^2\equiv 0(\mbox{mod}\ x^n)$ .

于是可以得到 $B\equiv 2B’-A\cdot B’^2(\mbox{mod}\ x^n)$ . 可以看出,多项式 $A$ 有逆元的充要条件是常数项 $A_0$ 有逆元.

时间复杂度为 $\Theta(n\log n)$ .

实现时可以把递归改成迭代,常数会优秀许多.

Luogu 4721 分治FFT

可以考虑 cdq 分治,每次处理 $[l,mid)$ 对 $[mid,r)$ 的贡献,时间复杂度 $O(n\log^2 n)$ .

另一个做法是记 $F,G$ 分别表示数列 $f,g$ 的生成函数,将它们卷起来,可以发现 $F(x)G(x)=F(x)-F(0)$ .

则 $F(x)=\frac{F(0)}{1-G(x)}$ ,写个多项式求逆,时间复杂度 $O(n\log n)$ .

Luogu P4512 多项式除法/多项式取模

给定一个 $n$ 次多项式 $A$ ,一个 $m$ 次多项式 $B$ ,满足 $m\le n$ ,求多项式 $D,R$ ,使得,
$$
A(x)=D(x)B(x)+R(x)
$$
并且 $\deg (D)\le n-m,\deg (R)<m$ .

尝试先消去余式 $R(x)$ 的影响,考虑多项式的 系数反转 ,即对于一个 $n$ 次多项式 $A$ ,
$$
A^R(x)=x^n\cdot A(\frac 1 x)
$$
如 $A(x)=x^3+2x^2+3x+4$ ,则 $A^R(x)=x^3\cdot A(\frac 1 x)=4x^3+3x^2+2x+1$ .

我们要求满足 $A(x)=D(x)B(x)+R(x)$ 的 $D,R$ ,如果将 $x$ 全部用 $\frac 1 x$ 替换,等式仍然成立.

替换后同时乘上 $x^n$ ,由于 $\deg(D)\le n-m,\deg(R)<m$ ,将 $D,R$ 次数分别看做 $n-m,m-1$ ,用 $0$ 补够.

于是可以得到,
$$
A^R(x)=D^R(x)B^R(x)+x^{n-m+1}\cdot R^R(x)
$$
我们将上面的等式两边都对 $x^{n-m+1}$ 取模, $x^{n-m+1}\cdot R^R(x)$ 就被消去了,于是得到,
$$
A^R(x)\equiv D^R(x)B^R(x) (\mbox{mod}\ x^{n-m+1})
$$
对 $B^R(x)$ 用一次多项式求逆,再用一次多项式乘法求得模 $x^{n-m+1}$ 意义下的 $D^R(x)$ .

由于 $\deg (D)\le n-m$ ,反转后 $\deg (D^R)\le n-m$ .所以模意义下求得的 $D^R(x)$ 就是真实的 $D^R(x)$ .

再系数反转求得 $D(x)$ ,回代 $A(x)=D(x)B(x)+R(x)$ 得到 $R(x)$ .

从上述过程可以看出,多项式除法/多项式取模的时间复杂度与多项式求逆相同,为 $\Theta(n\log n)$ .

Luogu P4728 多项式 $\ln$

首先需要了解多项式的求导和不定积分.对于一个 $n-1$ 次多项式 $A$ ,

$$
A(x)=\sum_{i=0}^{n-1} a_i\cdot x_i\\
A’(x)=\sum_{i=0}^{n-2} a_{i+1}\cdot(i+1)\cdot x^i \\
\int A(x) \mbox d x=\sum_{i=1}^{n}\frac {a_{i-1}} i \cdot x^i+C
$$

现在给出 $n-1$ 次多项式 $A(x)$ ,要在模 $x^n$ 意义下求 $B(x)$ ,使得 $B(x)\equiv \ln (A(x))\ (\mbox{mod}\ x^n)$ .

两边同时求导,得到 $B’(x)\equiv \frac {A’(x)} {A(x)}\ (\mbox{mod}\ x^n)$ .

多项式求逆得到 $\frac 1 {A(x)}$ ,再算出 $B’(x)$ ,再对 $B’(x)$ 不定积分得到 $B(x)$ .

这里 $B(x)$ 的常数项是 $0$ ,因为 $a_0=1$ ,若将 $\ln$ 函数大力展开,就可以发现 $B$ 的常数项就是 $\ln a_0$ .

时间复杂度 $\Theta(n\log n)$ .

多项式牛顿迭代

已知一个函数 $G(x)$ ,在模 $x^n$ 意义下求一个多项式 $F(x)\ \mbox{mod}\ x^n$ ,使得 $G(F(x))\equiv 0(\mbox{mod}\ x^n)$ .

仍然将项数用 $0$ 补到 $2$ 的幂次.当 $n=1$ 时,需要单独求解 $G(F(x))\equiv 0(\mbox{mod}\ x)$ .

否则,先求解 $F_0(x)$ ,使得 $G(F_0(x))\equiv 0 (\mbox{mod}\ x^{\frac n 2})$ .

考虑如何拓展到模 $x^n$ 下,把 $G(F(x))$ 在 $F_0(x)$ 处进行泰勒展开,

$$
G(F(x))=G(F_0(x))+\frac{G’(F_0(x))}{1!}\cdot (F(x)-F_0(x))+\frac{G’’(F_0(x))}{2!}\cdot (F(x)-F_0(x))^2 + \dots
$$

因为 $G(F(x))\equiv 0(\mbox{mod}\ x^n)$ ,所以 $G(F(x))\equiv 0(\mbox{mod}\ x^{\frac n 2})$ 也成立.

而 $G(F_0(x))\equiv 0 (\mbox{mod}\ x^{\frac n 2})$ 所以 $F(x)$ 与 $F_0(x)$ 次数低于 $x^{\frac n 2}$ 的部分是相同的.

所以展开式中从第三项 $\frac{G’’(F_0(x))}{2!}\cdot (F(x)-F_0(x))^2$ 起,在模 $x^n$ 意义下都为 $0$ .于是只保留前两项,得到

$$
G(F(x))\equiv G(F_0(x))+{G’(F_0(x))}\cdot (F(x)-F_0(x))\ (\mbox{mod}\ x^n)
$$

而 $G(F(x))\equiv 0(\mbox{mod}\ x^n)$ ,所以就有

$$
F(x)\equiv F_0(x)-\frac {G(F_0(x))} {G’(F_0(x))}\ (\mbox{mod}\ x^n)
$$

需要注意,这里的 $G’(F_0(x))$ 是以 $F_0(x)$ 作为自变量求导,而不是以 $x$ 作为自变量求导.

如,若 $G(x)=\ln x,F_0(x)=x$ ,则 $G’(F_0(x))=\frac 1 {F_0(x)}=\frac 1 x$ ,而不是 $\ln(1)=0$ .

时间复杂度为 $O(n\log n)$ ,由于可以实现类似解方程的操作,所以用途比较广泛.

如实现多项式开根,就可以直接设 $G(x)=x^2-A$ .

Luogu P4726 多项式 $\exp$

给定项数为 $n$ 的多项式 $A(x)$ ,在 $\mbox{mod} \ x^n$ 意义下求多项式 $B(x)$ ,使得 $B(x)\equiv \exp(A(x))\ (\mbox{mod} \ x^n)$ .

取对数,得到 $\ln B\equiv A \ (\mbox{mod} \ x^n)$ ,令 $G(x)=\ln x-A$ .

则问题等价于求解 $F(x)$ ,使得 $G(F(x))\equiv 0(\mbox{mod}\ x^n)$ .

直接套用牛顿迭代的那一套理论,得到
$$
F\equiv (1-{\ln (F_0)+A})\cdot F_0\ (\mbox{mod}\ x^n)
$$
递归求解,当 $n=1$ 时,令 $F_0(x)=\exp a_0$ 即可.一般会保证多项式 $A$ 的常数项 $a_0=0$ .

Luogu P5245 多项式快速幂

给定项数为 $n$ 的多项式 $A(x)$ ,正整数 $k$ .

在 $\mbox{mod}\ x^n$ 下求多项式 $B(x)$ ,使得 $B(x)\equiv A^k(x)\ (\mbox{mod} \ x^n)$ .

两边同时取对数,得到 $\ln B(x)\equiv k\ln A(x) \pmod {x^n}$ ,可以看出 $k$ 可以直接对 $P$ 取模.

再对两边同时做一次 $\exp$ ,得到 $B(x)\equiv \exp(k\ln A(x)) \pmod {x^n}$ .

Luogu P5050 多项式多点求值

给定一个 $n$ 次多项式 $F(x)$ 和 $m$ 个数 $a_i$ ,需要求出每个 $F(a_i)$ 的值.

定义减法卷积 ${\rm MULT}(F(x),G(x))=\sum_{i\ge0}(\sum_{j\ge 0}f_{i+j}g_j)x^i​$ ,可以将一个多项式取反后再卷积来实现.

有 $F(a_i)=[x^0]{\rm MULT}(F(x),\frac{1}{1-a_ix})$ ,且 ${\rm MULT}(F(x),G(x)H(x))={\rm MULT}({\rm MULT}(F(x),G(x)),H(x))​$ .

分治地来求每个 $F(a_i)​$ .
$$
G(l,r,x):={\rm MULT}(F(x),\frac 1{\prod_{i=l}^r(1-a_ix)}) \bmod x^{r-l+1}\\
G(l,mid,x)={\rm MULT}(G(l,r,x),\prod_{i=mid+1}^r (1-a_ix))\bmod x^{mid-l+1}\\
G(mid+1,r,x)={\rm MULT}(G(l,r,x),\prod_{i=l}^{mid} (1-a_ix)) \bmod x^{r-mid}
$$

当递归到 $l=r​$ 时,此时的 $G(l,r,x)​$ 的常数项就是我们要求的点值了.

每个要用到的 $\prod_{i=l}^r (1-a_ix)$ 可以用分治 NTT 预处理出来,时间复杂度 $O(n\log^2 n)​$ .

code

Loj 150 挑战多项式

给出次数为 $n$ 的多项式 $F(x)$ ,求
$$
\displaystyle G(x) \equiv \left({\left({1+\ln\left({2+F(x)-F(0)-{\exp\left({\int_0^x\frac{1}{\sqrt{F(t)}}\textrm{d}t}\right)}}\right)}\right)^k}\right)^\prime \pmod {x^n}
$$

在代码里面把多项式除法/取模也加上了.

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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
//%std
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int P=998244353,G=3;
int add(int a,int b)
{
return (a+b>=P)?(a+b-P):(a+b);
}
void inc(int &a,int b)
{
a=add(a,b);
}
int mul(int a,int b)
{
return 1LL * a * b % P;
}
int fpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
int sqr;
struct Complex
{
int r,i;
Complex(int r=0,int i=0):r(r),i(i) {}
Complex operator * (const Complex &rhs) const
{
int x=add(mul(r,rhs.r),mul(mul(i,rhs.i),sqr));
int y=add(mul(r,rhs.i),mul(rhs.r,i));
return Complex(x,y);
}
};
Complex Complex_Fpow(Complex a,int b)
{
Complex res=Complex(1,0);
while(b)
{
if(b&1)
res=res*a;
a=a*a;
b>>=1;
}
return res;
}
int Cipolla(int x)
{
if(fpow(x,(P-1)>>1)==P-1)
return -1;
srand(time(0));
int a;
while(1)
{
a=rand()%P;
if(fpow(add(mul(a,a),P-x),(P-1)>>1)==P-1)
{
sqr=add(mul(a,a),P-x);
break;
}
}
Complex tmp=Complex_Fpow(Complex(a,1),(P+1)>>1);
int res=tmp.r;
return min(res,add(P,-res));
}
const int MAXN=4e5+10;
int rev[MAXN],omega[MAXN],inv[MAXN],curn=0;
void init(int n)
{
if(curn==n)
return;
for(int i=0;i<n;++i)
rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
for(int l=2;l<=n;l<<=1)
{
omega[l]=fpow(G,(P-1)/l);
inv[l]=fpow(omega[l],P-2);
}
curn=n;
}
void DFT(int *a,int n,bool invflag)
{
init(n);
for(int i=0;i<n;++i)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int l=2;l<=n;l<<=1)
{
int m=l>>1;
int gi=omega[l];
if(invflag)
gi=inv[l];
for(int *p=a;p!=a+n;p+=l)
{
int g=1;
for(int i=0;i<m;++i)
{
int t=mul(g,p[i+m]);
p[i+m]=add(p[i],P-t);
p[i]=add(p[i],t);
g=mul(g,gi);
}
}
}
if(invflag)
{
int invn=fpow(n,P-2);
for(int i=0;i<n;++i)
a[i]=mul(a[i],invn);
}
}
void NTT(int *A,int *B,int *C,int lenA,int lenB)
{
int lenC=lenA+lenB-1,n=1;
while(n<lenC)
n<<=1;
static int a[MAXN],b[MAXN];
copy(A,A+lenA,a);
fill(a+lenA,a+n,0);
copy(B,B+lenB,b);
fill(b+lenB,b+n,0);
DFT(a,n,false);
DFT(b,n,false);
for(int i=0;i<n;++i)
C[i]=mul(a[i],b[i]);
DFT(C,n,true);
}
void PolyInverse(int *A,int *B,int N) // B=A^(-1)
{
int n=1;
while(n<N)
n<<=1;
static int res[MAXN],tmp[MAXN];
res[0]=fpow(A[0],P-2);
for(int i=2;i<=n;i<<=1)
{
NTT(A,res,tmp,i,i);
NTT(tmp,res,tmp,i,i);
for(int j=0;j<i;++j)
res[j]=add(mul(2,res[j]),P-tmp[j]);
}
copy(res,res+N,B);
}
void PolySqrt(int *A,int *B,int N) // B=sqrt(A)
{
int n=1;
while(n<N)
n<<=1;
static int res[MAXN],tmp[MAXN];
res[0]=Cipolla(A[0]);
for(int i=2;i<=n;i<<=1)
{
PolyInverse(res,tmp,i);
NTT(tmp,A,tmp,i,i);
for(int j=0;j<i;++j)
res[j]=mul((P+1)>>1,add(res[j],tmp[j]));
}
copy(res,res+N,B);
}
void PolyDiff(int *A,int n)
{
for(int i=0;i<n-1;++i)
A[i]=mul(i+1,A[i+1]);
A[n-1]=0;
}
void PolyInt(int *A,int n)
{
for(int i=n+1;i>=1;--i)
A[i]=mul(fpow(i,P-2),A[i-1]);
A[0]=0;
}
void PolyLn(int *A,int *B,int n) // B=ln(A)
{
static int invA[MAXN],tmp[MAXN];
PolyInverse(A,invA,n);
copy(A,A+n,tmp);
PolyDiff(tmp,n);
NTT(tmp,invA,tmp,n,n);
PolyInt(tmp,n);
copy(tmp,tmp+n,B);
}
void PolyExp(int *A,int *B,int N) // B=exp(A)
{
int n=1;
while(n<N)
n<<=1;
static int res[MAXN],tmp[MAXN];
res[0]=1;
for(int i=2;i<=n;i<<=1)
{
PolyLn(res,tmp,i);
for(int j=0;j<i;++j)
tmp[j]=add(A[j],P-tmp[j]);
inc(tmp[0],1);
NTT(tmp,res,res,i,i);
}
copy(res,res+N,B);
}
void PolyPow(int *A,int *B,int n,int k) // B=A^k
{
static int tmp[MAXN];
PolyLn(A,tmp,n);
for(int i=0;i<n;++i)
tmp[i]=mul(tmp[i],k);
PolyExp(tmp,B,n);
}
void PolyDivision(int *A,int *B,int *D,int *R,int n,int m) // A=B*D+R len(A)=n len(B)=m
{
static int ModA[MAXN],ModB[MAXN],InvB[MAXN];
--n,--m;
reverse(A,A+n+1);
reverse(B,B+m+1);
copy(A,A+n-m+1,ModA);
copy(B,B+n-m+1,ModB);
int N=1;
while(N<n-m+1)
N<<=1;
PolyInverse(ModB,InvB,N);
fill(InvB+n-m+1,InvB+N,0);
NTT(ModA,InvB,D,n-m+1,n-m+1);
reverse(D,D+n-m+1);
reverse(A,A+n+1);
reverse(B,B+m+1);
NTT(B,D,R,m,n-m+1);
for(int i=0;i<n;++i)
R[i]=add(A[i],P-R[i]);
}
int n,k,f[MAXN],g[MAXN];
int main()
{
n=read()+1,k=read();
for(int i=0;i<n;++i)
f[i]=g[i]=read();
PolySqrt(g,g,n);
PolyInverse(g,g,n);
PolyInt(g,n);
PolyExp(g,g,n);
for(int i=0;i<n;++i)
g[i]=add(f[i],P-g[i]);
inc(g[0],add(2,P-f[0]));
PolyLn(g,g,n);
inc(g[0],1);
PolyPow(g,g,n,k);
PolyDiff(g,n);
for(int i=0;i<n-1;++i)
printf("%d ",g[i]);
puts("");
return 0;
}