前言
突然发现自己好久都没写过博客了…赶紧来补一篇。
[其实自己写过几篇的…然而写到一半就咕掉了…]
相信总有一天会咕回来的
[回归正题]
最近学了有关多项式计算的一些知识。
于是蒟蒻完全进入了挂机状态。
作者用了两篇文章介绍了多项式全家桶的大部分成员[见标题]。
[学习笔记]多项式相关运算2
约定
对于一个多项式
A
(
x
)
A(x)
A(x),称其最高项的次数为这个多项式的度(degree),记作
d
e
g
A
deg_A
degA
对于多项式
A
(
x
)
,
B
(
x
)
A(x),B(x)
A(x),B(x),存在唯一的
Q
(
x
)
,
R
(
x
)
Q(x),R(x)
Q(x),R(x)满足
A
(
x
)
=
Q
(
x
)
B
(
x
)
+
R
(
x
)
A(x)=Q(x)B(x)+R(x)
A(x)=Q(x)B(x)+R(x),其中
d
e
g
R
<
d
e
g
B
deg_R<deg_B
degR<degB,我们称
Q
(
x
)
Q(x)
Q(x)为
B
(
x
)
B(x)
B(x)除
A
(
x
)
A(x)
A(x)的商,
R
(
x
)
R(x)
R(x)为
B
(
x
)
B(x)
B(x)除
A
(
x
)
A(x)
A(x)的余数,可以记作
A
(
x
)
≡
R
(
x
)
(
m
o
d
B
(
x
)
)
A(x)≡R(x)(mod B(x))
A(x)≡R(x)(modB(x))
多项式求逆
问题描述
已知一个多项式
A
(
x
)
A(x)
A(x)。如果存在一个多项式
B
(
x
)
B(x)
B(x)。
满足:1.
d
e
g
A
≥
d
e
g
B
deg_A≥deg_B
degA≥degB
2.
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
x
n
)
A(x)B(x)≡1(mod x^n)
A(x)B(x)≡1(modxn)
我们称
B
(
x
)
B(x)
B(x)为
A
(
x
)
m
o
d
A(x)mod
A(x)mod
x
n
x^n
xn意义下的逆元,记为
A
−
1
(
x
)
A^{-1}(x)
A−1(x)
分治
考虑用分治来解决这个问题。
当
n
=
1
n=1
n=1时,
A
(
x
)
≡
c
(
m
o
d
x
)
A(x)≡c(mod x)
A(x)≡c(modx),即
A
(
x
)
c
−
1
≡
1
(
m
o
d
x
)
A(x)c^{-1}≡1(mod x)
A(x)c−1≡1(modx),
A
(
x
)
−
1
=
c
−
1
A(x)^{-1}=c^{-1}
A(x)−1=c−1。
当
n
>
1
n>1
n>1时
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
A(x)B(x)≡1(mod
A(x)B(x)≡1(mod
x
n
)
x^n)
xn)
(
1
)
(1)
(1)
设
B
′
(
x
)
B'(x)
B′(x)为
A
(
x
)
A(x)
A(x)在
m
o
d
mod
mod
x
⌈
n
2
⌉
x^{\lceil \frac{n}{2} \rceil}
x⌈2n⌉的逆元,假设我们现在已经知道
B
′
(
x
)
B'(x)
B′(x),则:
A
(
x
)
B
′
(
x
)
≡
1
(
m
o
d
A(x)B'(x)≡1(mod
A(x)B′(x)≡1(mod
x
⌈
n
2
⌉
)
x^{\lceil \frac{n}{2} \rceil})
x⌈2n⌉)
(
2
)
(2)
(2)
显然
(
1
)
(1)
(1)式可变换为:
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
A(x)B(x)≡1(mod
A(x)B(x)≡1(mod
x
⌈
n
2
⌉
)
x^{\lceil \frac{n}{2} \rceil})
x⌈2n⌉)
(
3
)
(3)
(3)
(
2
)
−
(
3
)
(2)-(3)
(2)−(3)式得
A
(
x
)
(
B
(
x
)
−
B
′
(
x
)
)
≡
0
(
m
o
d
A(x)(B(x)-B'(x))≡0(mod
A(x)(B(x)−B′(x))≡0(mod
x
⌈
n
2
⌉
)
x^{\lceil \frac{n}{2} \rceil})
x⌈2n⌉)
即
B
(
x
)
−
B
′
(
x
)
≡
0
(
m
o
d
B(x)-B'(x)≡0(mod
B(x)−B′(x)≡0(mod
x
⌈
n
2
⌉
)
x^{\lceil \frac{n}{2} \rceil})
x⌈2n⌉)
两边平方一下
B
2
(
x
)
−
2
B
(
x
)
B
′
(
x
)
+
B
′
2
(
x
)
≡
0
(
m
o
d
B^2(x)-2B(x)B'(x)+B'^2(x)≡0(mod
B2(x)−2B(x)B′(x)+B′2(x)≡0(mod
x
n
)
x^n)
xn)
这里解释一下为什么平方可以将膜长平方
我们知道
B
(
x
)
−
B
′
(
x
)
≡
0
(
m
o
d
B(x)-B'(x)≡0(mod
B(x)−B′(x)≡0(mod
x
⌈
n
2
⌉
)
x^{\lceil \frac{n}{2} \rceil})
x⌈2n⌉)
可以理解为取模之后各项的系数都为0。
B
(
x
)
−
B
′
(
x
)
B(x)-B'(x)
B(x)−B′(x)在
m
o
d
mod
mod
x
⌈
n
2
⌉
x^{\lceil \frac{n}{2} \rceil}
x⌈2n⌉下只有
⌈
n
2
⌉
\lceil \frac{n}{2} \rceil
⌈2n⌉项。
平方之后,有
n
n
n项,我们知道一个项的系数为
∑
j
=
0
i
a
j
a
i
−
j
\sum^i_{j=0}a_ja_{i-j}
∑j=0iajai−j,
而
i
i
i,
i
−
j
i-j
i−j必有一项小于
⌈
n
2
⌉
\lceil \frac{n}{2} \rceil
⌈2n⌉。所以平方之后
m
o
d
mod
mod
x
n
x^n
xn也是
0
0
0。
两边同乘以
A
(
x
)
A(x)
A(x)得
B
2
(
x
)
A
(
x
)
−
2
B
(
x
)
B
′
(
x
)
A
(
x
)
+
B
′
2
(
x
)
A
(
x
)
≡
0
(
m
o
d
B^2(x)A(x)-2B(x)B'(x)A(x)+B'^2(x)A(x)≡0(mod
B2(x)A(x)−2B(x)B′(x)A(x)+B′2(x)A(x)≡0(mod
x
n
)
x^n)
xn)
注意到
A
(
x
)
B
(
x
)
≡
1
(
m
o
d
A(x)B(x)≡1(mod
A(x)B(x)≡1(mod
x
n
)
x^n)
xn)
那么有
B
(
x
)
−
2
B
′
(
x
)
+
B
′
2
(
x
)
A
(
x
)
≡
0
(
m
o
d
B(x)-2B'(x)+B'^2(x)A(x)≡0(mod
B(x)−2B′(x)+B′2(x)A(x)≡0(mod
x
n
)
x^n)
xn)
移项
B
(
x
)
≡
2
B
′
(
x
)
−
B
′
2
(
x
)
A
(
x
)
(
m
o
d
B(x)≡2B'(x)-B'^2(x)A(x)(mod
B(x)≡2B′(x)−B′2(x)A(x)(mod
x
n
)
x^n)
xn)
使用多项式乘法和减法即可求出
B
(
x
)
B(x)
B(x)
分析一下复杂度:
T
(
n
)
=
T
(
n
2
)
+
O
(
n
l
o
g
n
)
=
O
(
n
l
o
g
n
)
T(n)=T(\frac{n}{2})+O(nlogn)=O(nlogn)
T(n)=T(2n)+O(nlogn)=O(nlogn)
注意不是
O
(
n
O(n
O(n
l
o
g
2
n
)
log^2n)
log2n)。因为n的规模在减半,复杂度介于
O
(
n
O(n
O(n
l
o
g
n
)
logn)
logn)与
O
(
n
O(n
O(n
l
o
g
2
n
)
log^2n)
log2n)之间。
模板
[仅供参考]
[LuoguP4238]【模板】多项式求逆
void mod_Inv(int deg,Poly &a,Poly &b)
//B(x)=2B'(x)-A(x)*B'^2(x)
{
static Poly c;
if(deg==1){b[0]=fst_pow(a[0],MOD-2);return ;}
mod_Inv((deg+1)>>1,a,b);
int len=1;
while(len<((deg<<1)-1))len<<=1;
copy(a,a+deg,c);
fill(c+deg,c+len,0);NTT(c,len,1);
fill(b+deg,b+len,0);NTT(b,len,1);
for(int i=0;i<len;i++)
b[i]=1LL*(2-1LL*c[i]*b[i]%MOD+MOD)%MOD*b[i]%MOD;
NTT(b,len,-1);
fill(b+deg,b+len,0);
}
ex模板
EX:[LuoguP4239]【模板】多项式求逆(加强版)
任意模数NTT=n个NTT+CRT
多项式除法
问题描述
给定两个多项式
A
(
x
)
A(x)
A(x),
B
(
x
)
B(x)
B(x)
约定
n
=
d
e
g
A
,
m
=
d
e
g
B
n=deg_A,m=deg_B
n=degA,m=degB。满足
n
≥
m
n≥m
n≥m。
找到一个多项式
D
(
x
)
D(x)
D(x)
满足:
1.
A
(
x
)
=
D
(
x
)
B
(
x
)
+
P
(
x
)
A(x)=D(x)B(x)+P(x)
A(x)=D(x)B(x)+P(x),其中
A
(
x
)
A(x)
A(x)为被除数,
B
(
x
)
B(x)
B(x)为除数,
P
(
x
)
P(x)
P(x)为余数。
2.
d
e
g
D
≤
n
−
m
deg_D≤n-m
degD≤n−m,
d
e
g
R
<
m
deg_R<m
degR<m
UPD:需要注意的是,这里的除法指的是整除
是整数意义上的除法,与前文的求逆有所不同。
计算
考虑直接计算。
方便起见,我们定义
A
R
(
x
)
A^R(x)
AR(x)为原多项式的系数反转后的多项式。
例如
A
(
x
)
=
2
x
3
+
3
x
2
+
3
x
+
666
A(x)=2x^3+3x^2+3x+666
A(x)=2x3+3x2+3x+666,则
A
R
(
x
)
=
666
x
3
+
3
x
2
+
3
x
+
2
A^R(x)=666x^3+3x^2+3x+2
AR(x)=666x3+3x2+3x+2
容易发现
A
R
(
x
)
=
x
n
A
(
1
x
)
A^R(x)=x^nA(\frac{1}{x})
AR(x)=xnA(x1).[自己算算就知道了]
回归我们最开始的式子
A
(
x
)
=
D
(
x
)
B
(
x
)
+
P
(
x
)
A(x)=D(x)B(x)+P(x)
A(x)=D(x)B(x)+P(x)
将
1
x
\frac{1}{x}
x1代入
A
(
1
x
)
=
D
(
1
x
)
B
(
1
x
)
+
P
(
1
x
)
A(\frac{1}{x})=D(\frac{1}{x})B(\frac{1}{x})+P(\frac{1}{x})
A(x1)=D(x1)B(x1)+P(x1)
两边同乘
x
n
x^n
xn得
x
n
A
(
1
x
)
=
x
n
D
(
1
x
)
B
(
1
x
)
+
x
n
P
(
1
x
)
x^nA(\frac{1}{x})=x^nD(\frac{1}{x})B(\frac{1}{x})+x^nP(\frac{1}{x})
xnA(x1)=xnD(x1)B(x1)+xnP(x1)
分配一下
x
n
A
(
1
x
)
=
x
n
−
m
D
(
1
x
)
x
m
B
(
1
x
)
+
x
n
−
m
+
1
x
m
−
1
P
(
1
x
)
x^nA(\frac{1}{x})=x^{n-m}D(\frac{1}{x})x^mB(\frac{1}{x})+x^{n-m+1}x^{m-1}P(\frac{1}{x})
xnA(x1)=xn−mD(x1)xmB(x1)+xn−m+1xm−1P(x1)
代换一下
A
R
(
x
)
=
D
R
(
x
)
B
R
(
x
)
+
x
n
−
m
+
1
P
R
(
x
)
A^R(x)=D^R(x)B^R(x)+x^{n-m+1}P^R(x)
AR(x)=DR(x)BR(x)+xn−m+1PR(x)
由于我们要求的是
D
(
x
)
D(x)
D(x),而现在只有
P
R
(
x
)
P^R(x)
PR(x)的系数不为
1
1
1,我们肯定要想办法去掉
P
R
(
x
)
P^R(x)
PR(x)。
我们可以两边同时
m
o
d
mod
mod
x
n
−
m
+
1
x^{n-m+1}
xn−m+1,将
P
R
(
x
)
P^R(x)
PR(x)消掉。
同时,
D
(
x
)
D(x)
D(x)的最高次项是
n
−
m
n-m
n−m次,也不会对我们求解
D
(
x
)
D(x)
D(x)有任何影响。
得
A
R
(
x
)
≡
D
R
(
x
)
B
R
(
x
)
(
m
o
d
A^R(x)≡D^R(x)B^R(x)(mod
AR(x)≡DR(x)BR(x)(mod
x
n
−
m
+
1
)
x^{n-m+1})
xn−m+1)
移项
A
R
(
x
)
B
R
−
1
(
x
)
≡
D
R
(
x
)
(
m
o
d
A^R(x)B^{R^{-1}}(x)≡D^R(x)(mod
AR(x)BR−1(x)≡DR(x)(mod
x
n
−
m
+
1
)
x^{n-m+1})
xn−m+1)
我们只需求
B
R
−
1
(
x
)
B^{R^{-1}}(x)
BR−1(x),并与
A
R
(
x
)
A^R(x)
AR(x)做乘法即可。
模板
[仅供参考]
void mod_div(Poly &a,Poly &p,int n,int m)
{
static Poly tmp,p0,p1,D;
copy(a,a+n,tmp);
reverse(tmp,tmp+n);
copy(p,p+m,p0);
reverse(p0,p0+m);
mod_Inv(n-m+1,p0,p1);
int len=1;
while(len<(n-m+1)*2-1)len<<=1;
fill(tmp+n-m+1,tmp+len,0);
NTT(tmp,len,1);
fill(p1+n-m+1,p1+len,0);
NTT(p1,len,1);
for(int i=0;i<len;i++)
D[i]=(1LL*tmp[i]*p1[i])%MOD;
NTT(D,len,-1);
fill(D+n-m+1,D+len,0);
reverse(D,D+n-m+1);
}
多项式取模
问题描述
给定两个多项式
A
(
x
)
A(x)
A(x),
B
(
x
)
B(x)
B(x)
约定
n
=
d
e
g
A
,
m
=
d
e
g
B
n=deg_A,m=deg_B
n=degA,m=degB。满足
n
≥
m
n≥m
n≥m。
找到一个多项式
P
(
x
)
P(x)
P(x)
满足:
1.
A
(
x
)
=
D
(
x
)
B
(
x
)
+
P
(
x
)
A(x)=D(x)B(x)+P(x)
A(x)=D(x)B(x)+P(x),其中
A
(
x
)
A(x)
A(x)为被除数,
B
(
x
)
B(x)
B(x)为除数,
D
(
x
)
D(x)
D(x)为商。
2.
d
e
g
D
≤
n
−
m
deg_D≤n-m
degD≤n−m,
d
e
g
R
<
m
deg_R<m
degR<m
计算
有了上一节的计算,求
P
(
x
)
P(x)
P(x)就简单多了。
直接将式子变形
P
(
x
)
=
A
(
x
)
−
D
(
x
)
B
(
x
)
P(x)=A(x)-D(x)B(x)
P(x)=A(x)−D(x)B(x)
先求
D
(
x
)
D(x)
D(x),再求
P
(
x
)
P(x)
P(x)即可。
模板
[仅供参考]
void mod_MOD(Poly &a,Poly &p,int n,int m)
//R(x)=A(x)-D(x)*B(x)
//先求D(x),再求R(x)->A'(x)
{
static Poly tmp,p0,p1,D;
copy(a,a+n,tmp);
reverse(tmp,tmp+n);
copy(p,p+m,p0);
reverse(p0,p0+m);
mod_Inv(n-m+1,p0,p1);
int len=1;
while(len<(n-m+1)*2-1)len<<=1;
fill(tmp+n-m+1,tmp+len,0);
NTT(tmp,len,1);
fill(p1+n-m+1,p1+len,0);
NTT(p1,len,1);
for(int i=0;i<len;i++)
D[i]=(1LL*tmp[i]*p1[i])%MOD;
NTT(D,len,-1);
fill(D+n-m+1,D+len,0);
reverse(D,D+n-m+1);
len=1;
while(len<n)len<<=1;
NTT(D,len,1);
fill(p0+m,p0+len,0);
reverse(p0,p0+m);
NTT(p0,len,1);
for(int i=0;i<len;i++)D[i]=(1LL*D[i]*p0[i])%MOD;
NTT(D,len,-1);
for(int i=0;i<n;i++)a[i]=(a[i]-D[i]+MOD)%MOD;
}
应用:常系数齐次线性递推
[CodeChef RNG]Random Number Generator
右转dalao博客
前面所有板子都用上了
[调了两个多小时,调着调着就跟dalao的代码没什么区别了…]
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int MAXN=30005,MOD=104857601,G=3;
#define LL long long
typedef int Poly[MAXN*5];
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*10+s-'0';s=getchar();}
return x*f;
}
int fst_pow(int x,int y)
{
int res=1;
while(y){
if(y&1)res=((LL)x*res)%MOD;
x=((LL)x*x)%MOD,y>>=1;
}return res;
}
void print(Poly &a,int n)
{
for(int i=0;i<n;i++)
printf("%d ",a[i]);
printf("\n");
}
//Poly_Calc
void NTT(Poly &a,int n,int x)
{
for(int i=0,j=0;i<n;i++)
{
if(i<j)swap(a[i],a[j]);
int k=n>>1;
while(k&&(k&j))j^=k,k>>=1;
j^=k;
}
for(int i=1;i<n;i<<=1)
{
int gn=fst_pow(G,(MOD-1)/(i<<1)),g=1;
if(x==-1)gn=fst_pow(gn,MOD-2);
for(int j=0;j<i;j++,g=(1LL*g*gn)%MOD)
for(int l=j,r=l+i;l<n;l+=(i<<1),r=l+i)
{
int temp=(1LL*a[r]*g)%MOD;
a[r]=(a[l]-temp+MOD)%MOD;
a[l]=(a[l]+temp)%MOD;
}
}
if(x==1)return;
int ny=fst_pow(n,MOD-2);
for(int i=0;i<n;++i) a[i]=1LL*a[i]*ny%MOD;
}
void mod_Inv(int deg,Poly &a,Poly &b)
//B(x)=2C(x)-A(x)*C^2(x)
{
static Poly c;
if(deg==1){b[0]=fst_pow(a[0],MOD-2);return ;}
mod_Inv((deg+1)>>1,a,b);
int len=1;
while(len<((deg<<1)-1))len<<=1;
copy(a,a+deg,c);
fill(c+deg,c+len,0);NTT(c,len,1);
fill(b+deg,b+len,0);NTT(b,len,1);
for(int i=0;i<len;i++)
b[i]=1LL*(2-1LL*c[i]*b[i]%MOD+MOD)%MOD*b[i]%MOD;
NTT(b,len,-1);
fill(b+deg,b+len,0);
}
void mod_MOD(Poly &a,Poly &p,int n,int m)
//A(x)=D(x)B(x)+R(x) 求R(x)
//rev(D(x))=rev(A(x))*rev^-1(B(x))(mod x^(n-m+1))
//R(x)=A(x)-D(x)*B(x)
//先求D(x),再求R(x)->A'(x)
{
static Poly tmp,p0,p1,D;
copy(a,a+n,tmp);
reverse(tmp,tmp+n);
copy(p,p+m,p0);
reverse(p0,p0+m);
mod_Inv(n-m+1,p0,p1);
int len=1;
while(len<(n-m+1)*2-1)len<<=1;
fill(tmp+n-m+1,tmp+len,0);
NTT(tmp,len,1);
fill(p1+n-m+1,p1+len,0);
NTT(p1,len,1);
for(int i=0;i<len;i++)
D[i]=(1LL*tmp[i]*p1[i])%MOD;
NTT(D,len,-1);
fill(D+n-m+1,D+len,0);
reverse(D,D+n-m+1);
len=1;
while(len<n)len<<=1;
NTT(D,len,1);
fill(p0+m,p0+len,0);
reverse(p0,p0+m);
NTT(p0,len,1);
for(int i=0;i<len;i++)D[i]=(1LL*D[i]*p0[i])%MOD;
NTT(D,len,-1);
for(int i=0;i<n;i++)a[i]=(a[i]-D[i]+MOD)%MOD;
}
void mod_Mul(Poly &a,Poly &b,Poly &p,int n)
//注意取模
{
static Poly tmp;
copy(b,b+n,tmp);
int len=1;
while(len<(n<<1))len<<=1;
fill(a+n,a+len,0);
fill(tmp+n,tmp+len,0);
NTT(a,len,1),NTT(tmp,len,1);
for(int i=0;i<len;i++)a[i]=(1LL*a[i]*tmp[i])%MOD;
NTT(a,len,-1);
mod_MOD(a,p,n*2-1,n);
}
//END
void matrix_pow(Poly &res,Poly &a,LL b,Poly &p,int n)
{
fill(res,res+n,0);
res[0]=1;
while(b)
{
if(b&1LL)mod_Mul(res,a,p,n);
mod_Mul(a,a,p,n);
b>>=1LL;
}
}
LL n,k;
Poly A,C,P,t,f;
int main()
{
scanf("%lld%lld",&k,&n);
for(int i=0;i<k;i++)scanf("%d",&A[i]);
for(int i=0;i<k;i++)scanf("%d",&C[i]);
P[k]=1;//p为p(x)=1*x^k-C_1*x^{k-1}-....-C_{k-1}*x-C_k
for(int i=0;i<k;i++)
P[i]=(MOD-C[k-i-1])%MOD;//防止负数
t[1]=1;//相当于每次乘一个x[即向右移]
matrix_pow(f,t,n-1,P,k+1);//实际上并不是矩阵乘法
int ans=0;
for(int i=0;i<k;i++)ans=(ans+(1LL*A[i]*f[i])%MOD)%MOD;//
printf("%d",ans);
}
多项式多点求值
问题描述
给定一个多项式
A
(
x
)
A(x)
A(x)和坐标点集
X
=
{
x
0
,
x
1
,
x
2
,
.
.
.
,
x
n
−
1
}
X=\{x_0,x_1,x_2,...,x_{n-1}\}
X={x0,x1,x2,...,xn−1}。
求
A
(
x
1
)
,
A
(
x
2
)
,
A
(
x
3
)
,
.
.
.
,
A
(
x
n
)
A(x_1),A(x_2),A(x_3),...,A(x_n)
A(x1),A(x2),A(x3),...,A(xn)。
暴力
直接将每个
x
i
x_i
xi暴力代入求值。
复杂度
O
(
n
d
e
g
A
)
O(ndeg_A)
O(ndegA)。
[倒是可以套一下秦九韶算法之类的…]
分治
没错,还是分治。
尝试将点集
X
X
X分成两个部分。
X
[
0
]
=
{
x
0
,
x
1
,
x
2
,
.
.
.
,
x
⌊
n
2
⌋
}
X^{[0]}=\{x_0,x_1,x_2,...,x_{\lfloor \frac{n}{2} \rfloor}\}
X[0]={x0,x1,x2,...,x⌊2n⌋}
X
[
1
]
=
{
x
⌊
n
2
⌋
+
1
,
x
⌊
n
2
⌋
+
2
,
x
⌊
n
2
⌋
+
3
,
.
.
.
,
x
n
−
1
}
X^{[1]}=\{x_{\lfloor \frac{n}{2} \rfloor+1},x_{\lfloor \frac{n}{2} \rfloor+2},x_{\lfloor \frac{n}{2} \rfloor+3},...,x_{n-1}\}
X[1]={x⌊2n⌋+1,x⌊2n⌋+2,x⌊2n⌋+3,...,xn−1}
同时,我们必须能够把系数减半。
令点集
X
[
0
]
X^{[0]}
X[0]插值得到的多项式为
A
[
0
]
(
x
)
A^{[0]}(x)
A[0](x),点集
X
[
1
]
X^{[1]}
X[1]插值得到的多项式为
A
[
1
]
(
x
)
A^{[1]}(x)
A[1](x)。两个多项式的最高次都为
⌊
n
2
⌋
\lfloor \frac{n}{2} \rfloor
⌊2n⌋。
考虑构造多项式:
P
[
0
]
(
x
)
=
∏
i
=
0
⌊
n
2
⌋
(
x
−
x
i
)
P^{[0]}(x)=\prod^{\lfloor \frac{n}{2} \rfloor}_{i=0}{(x-x_i)}
P[0](x)=i=0∏⌊2n⌋(x−xi)
P
[
1
]
(
x
)
=
∏
i
=
⌊
n
2
⌋
+
1
n
(
x
−
x
i
)
P^{[1]}(x)=\prod^{n}_{i=\lfloor \frac{n}{2} \rfloor+1}{(x-x_i)}
P[1](x)=i=⌊2n⌋+1∏n(x−xi)
然而为什么我们要将多项式构造成这样呢?
容易发现当
x
∈
X
[
0
]
x\in X^{[0]}
x∈X[0]时,
P
[
0
]
=
0
P^{[0]}=0
P[0]=0,当
x
∈
X
[
1
]
x\in X^{[1]}
x∈X[1]时,
P
[
1
]
=
0
P^{[1]}=0
P[1]=0。
这样我们就可以将
A
[
0
]
(
x
)
A^{[0]}(x)
A[0](x)构造成:
A
[
0
]
(
x
)
=
D
(
x
)
P
[
0
]
(
x
)
+
A
(
x
)
A^{[0]}(x)=D(x)P^{[0]}(x)+A(x)
A[0](x)=D(x)P[0](x)+A(x)
同理
A
[
1
]
(
x
)
=
D
(
x
)
P
[
1
]
(
x
)
+
A
(
x
)
A^{[1]}(x)=D(x)P^{[1]}(x)+A(x)
A[1](x)=D(x)P[1](x)+A(x)
转换一下
A
[
0
]
(
x
)
≡
A
(
x
)
(
m
o
d
P
[
0
]
(
x
)
)
A^{[0]}(x)≡A(x)(mod P^{[0]}(x))
A[0](x)≡A(x)(modP[0](x))
A
[
1
]
(
x
)
≡
A
(
x
)
(
m
o
d
P
[
1
]
(
x
)
)
A^{[1]}(x)≡A(x)(mod P^{[1]}(x))
A[1](x)≡A(x)(modP[1](x))
这样,我们就可以保证
x
∈
X
[
0
]
x\in X^{[0]}
x∈X[0]时,
A
(
x
)
A(x)
A(x)与
A
[
0
]
(
x
)
A^{[0]}(x)
A[0](x)取相同的值,
x
∈
X
[
1
]
x\in X^{[1]}
x∈X[1]时,
A
(
x
)
A(x)
A(x)与
A
[
1
]
(
x
)
A^{[1]}(x)
A[1](x)取相同的值。
需要用到多项式取模,取模复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
分析一下复杂度:
T
(
n
)
=
2
T
(
n
2
)
+
O
(
n
l
o
g
n
)
=
O
(
n
l
o
g
2
n
)
T(n)=2T(\frac{n}{2})+O(nlogn)=O(nlog^2n)
T(n)=2T(2n)+O(nlogn)=O(nlog2n)
模板
[Lougu P5050]【模板】多项式多点求值
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
const int MAXN=600005,MOD=998244353,G=3;
#define LL long long
typedef int Poly[MAXN+5];
int ct=0,lc[MAXN+5],rc[MAXN+5];
vector<int> P[MAXN+5];
int A[18][MAXN+5];
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*10+s-'0';s=getchar();}
return x*f;
}
int fst_pow(int x,int y)
{
int res=1;
while(y){
if(y&1)res=(1LL*x*res)%MOD;
x=(1LL*x*x)%MOD,y>>=1;
}return res;
}
void print(Poly &a,int n)
{
for(int i=0;i<n;i++)
printf("%d ",a[i]);
printf("\n");
}
//Poly_Calc
void NTT(Poly &a,int n,int x)
{
for(int i=0,j=0;i<n;i++)
{
if(i<j)swap(a[i],a[j]);
int k=n>>1;
while(k&&(k&j))j^=k,k>>=1;
j^=k;
}
for(int i=1;i<n;i<<=1)
{
int gn=fst_pow(G,(MOD-1)/(i<<1)),g=1;
if(x==-1)gn=fst_pow(gn,MOD-2);
for(int j=0;j<i;j++,g=(1LL*g*gn)%MOD)
for(int l=j,r=l+i;l<n;l+=(i<<1),r=l+i)
{
int temp=(1LL*a[r]*g)%MOD;
a[r]=(a[l]-temp+MOD)%MOD;
a[l]=(a[l]+temp)%MOD;
}
}
if(x==1)return;
int ny=fst_pow(n,MOD-2);
for(int i=0;i<n;++i) a[i]=1LL*a[i]*ny%MOD;
}
void mod_Mul(Poly &a,Poly &b,Poly &res,int n)
{
static Poly a0,b0;
copy(a,a+n,a0);copy(b,b+n,b0);
NTT(a0,n,1);NTT(b0,n,1);
for(int i=0;i<n;i++)
a0[i]=(1LL*a0[i]*b0[i])%MOD;
NTT(a0,n,-1);
copy(a0,a0+n,res);
}
void mod_Inv(int deg,Poly &a,Poly &b)
//B(x)=2C(x)-A(x)*C^2(x)
{
static Poly c;
if(deg==1){b[0]=fst_pow(a[0],MOD-2);return ;}
mod_Inv((deg+1)>>1,a,b);
int len=1;
while(len<((deg<<1)-1))len<<=1;
copy(a,a+deg,c);
fill(c+deg,c+len,0);NTT(c,len,1);
fill(b+deg,b+len,0);NTT(b,len,1);
for(int i=0;i<len;i++)
b[i]=1LL*(2-1LL*c[i]*b[i]%MOD+MOD)%MOD*b[i]%MOD;
NTT(b,len,-1);
fill(b+deg,b+len,0);
}
void mod_MOD(Poly &res,Poly &a,Poly &p,int n,int m)
//A(x)=D(x)B(x)+R(x) 求R(x)
//rev(D(x))=rev(A(x))*rev^-1(B(x))(mod x^(n-m+1))
//R(x)=A(x)-D(x)*B(x)
//先求D(x),再求R(x)->A'(x)
{
static Poly tmp,p0,p1,D;
int len=1;while(len<=(n<<1))len<<=1;
for(int i=0;i<=len;i++)tmp[i]=p0[i]=p1[i]=D[i]=0;
copy(a,a+n+1,tmp);
reverse(tmp,tmp+n+1);
copy(p,p+m+1,p0);
reverse(p0,p0+m+1);
fill(p0+n-m+1,p0+m+1,0);
len=1;
while(len<=(n-m))len<<=1;
mod_Inv(len,p0,p1);
len=1;
while(len<=(n<<1))len<<=1;
mod_Mul(p1,tmp,D,len);
reverse(D,D+n-m+1);
fill(D+n-m+1,D+len,0);
mod_Mul(D,p,tmp,len);
for(int i=0;i<m;i++)res[i]=(a[i]-tmp[i]+MOD)%MOD;
}
//END
Poly B,X;
void get_P(int& id,int l,int r)
{
static Poly p0,p1,res;
id=++ct;
int len=1,mid=(l+r)>>1;
while(len<(r-l+2))len<<=1;
P[id].resize(len);
if(l==r)
{
P[id][0]=(MOD-(X[l-1]%MOD+MOD)%MOD);
P[id][1]=1;return ;
}
get_P(lc[id],l,mid);
get_P(rc[id],mid+1,r);
int Lsiz=P[lc[id]].size();
for(int i=0;i<Lsiz;i++)p0[i]=P[lc[id]][i];
fill(p0+Lsiz,p0+len,0);
int Rsiz=P[rc[id]].size();
for(int i=0;i<Rsiz;i++)p1[i]=P[rc[id]][i];
fill(p1+Rsiz,p1+len,0);
mod_Mul(p0,p1,res,len);
for(int i=0;i<len;i++)P[id][i]=res[i];
}
void point_eva(int dep,int id,int l,int r,int len)
{
static Poly nP;
int m=r-l+1;
fill(nP,nP+len+10,0);
for(int i=0;i<=m;i++)nP[i]=P[id][i];
if(len>=m)mod_MOD(A[dep],A[dep-1],nP,len,m);
else copy(A[dep-1],A[dep-1]+m,A[dep]);
if(l==r)
{printf("%d\n",A[dep][0]);return ;}
int mid=(l+r)>>1;
point_eva(dep+1,lc[id],l,mid,m-1);
point_eva(dep+1,rc[id],mid+1,r,m-1);
}
int n,m;
int main()
{
n=read(),m=read();
for(int i=0;i<=n;i++)A[0][i]=read();
for(int i=0;i<m;i++)X[i]=read();
int rt;
get_P(rt,1,m);
point_eva(1,rt,1,m,n);
//system("pause");
}
多项式快速插值
问题描述
点集
X
=
{
(
x
0
,
y
0
)
,
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
.
.
.
,
(
x
n
−
1
,
y
n
−
1
)
}
X=\{(x_0,y_0),(x_1,y_1),(x_2,y_2),...,(x_{n-1},y_{n-1})\}
X={(x0,y0),(x1,y1),(x2,y2),...,(xn−1,yn−1)}。
求
A
(
x
)
A(x)
A(x)。
暴力
列方程,高斯消元即可。
复杂度
O
(
n
3
)
O(n^3)
O(n3)。
分治
同理,我们先尝试将点集
X
X
X分成两个部分。
X
[
0
]
=
{
(
x
0
,
y
0
)
,
(
x
1
,
y
1
)
,
.
.
.
,
(
x
⌊
n
2
⌋
,
y
⌊
n
2
⌋
)
}
X^{[0]}=\{(x_0,y_0),(x_1,y_1),...,(x_{\lfloor \frac{n}{2} \rfloor},y_{\lfloor \frac{n}{2} \rfloor})\}
X[0]={(x0,y0),(x1,y1),...,(x⌊2n⌋,y⌊2n⌋)}
X
[
1
]
=
{
(
x
⌊
n
2
⌋
+
1
,
y
⌊
n
2
⌋
+
1
)
,
(
x
⌊
n
2
⌋
+
2
,
y
⌊
n
2
⌋
+
2
)
,
.
.
.
,
(
x
n
−
1
,
y
n
−
1
)
}
X^{[1]}=\{(x_{\lfloor \frac{n}{2} \rfloor+1},y_{\lfloor \frac{n}{2} \rfloor+1}),(x_{\lfloor \frac{n}{2} \rfloor+2},y_{\lfloor \frac{n}{2} \rfloor+2}),...,(x_{n-1},y_{n-1})\}
X[1]={(x⌊2n⌋+1,y⌊2n⌋+1),(x⌊2n⌋+2,y⌊2n⌋+2),...,(xn−1,yn−1)}
令点集
X
[
0
]
X^{[0]}
X[0]插值得到的多项式为
A
[
0
]
(
x
)
A^{[0]}(x)
A[0](x)。
假设我们已经知道了多项式
A
[
0
]
(
x
)
A^{[0]}(x)
A[0](x),我们现在要将其转换为
A
(
x
)
A(x)
A(x)。
构造多项式
P
(
x
)
=
∏
i
=
0
⌊
n
2
⌋
(
x
−
x
i
)
P(x)=\prod^{\lfloor \frac{n}{2} \rfloor}_{i=0}(x-x_i)
P(x)=i=0∏⌊2n⌋(x−xi)
考虑构造
A
(
x
)
=
A
[
1
]
(
x
)
P
(
x
)
+
A
[
0
]
(
x
)
A(x)=A^{[1]}(x)P(x)+A^{[0]}(x)
A(x)=A[1](x)P(x)+A[0](x)
其中
A
(
x
)
,
A
[
1
]
(
x
)
A(x),A^{[1]}(x)
A(x),A[1](x)都未知。
明显,这样构造当
(
x
i
,
y
i
)
∈
X
[
0
]
,
A
(
x
i
)
=
A
[
0
]
(
x
i
)
(x_i,y_i)\in X^{[0]},A(x_i)=A^{[0]}(x_i)
(xi,yi)∈X[0],A(xi)=A[0](xi)
那么我们现在的问题就是如何构造
A
[
1
]
(
x
)
A^{[1]}(x)
A[1](x)使得当
(
x
i
,
y
i
)
∈
X
[
1
]
,
A
(
x
1
)
=
A
[
1
]
(
x
i
)
(x_i,y_i)\in X^{[1]},A(x_1)=A^{[1]}(x_i)
(xi,yi)∈X[1],A(x1)=A[1](xi)。
那么有
y
i
=
A
[
1
]
(
x
i
)
P
(
x
i
)
+
A
[
0
]
(
x
i
)
y_i=A^{[1]}(x_i)P(x_i)+A{[0]}(x_i)
yi=A[1](xi)P(xi)+A[0](xi)。
化简得
A
[
1
]
(
x
i
)
=
y
i
−
A
[
0
]
(
x
i
)
P
(
x
i
)
A^{[1]}(x_i)=\frac{y_i-A{[0]}(x_i)}{P(x_i)}
A[1](xi)=P(xi)yi−A[0](xi)。
可以得到一个新的点集
X
′
[
1
]
X'^{[1]}
X′[1]。
用插值法求出
A
[
1
]
(
x
)
A^{[1]}(x)
A[1](x),再求
A
(
x
)
A(x)
A(x)即可。
模板
[Lougu P5158]【模板】多项式快速插值
UPD:我放弃挣扎了
例题 [51nod 1387] 移数字
关于题解…它已经鸽掉了…
感谢
感谢以下dalao的帮助:
1.yoyoball大佬:【learning】多项式相关(求逆、开根、除法、取模)
2.CaptainChen学长:【CodeChef RNG】Random Number Generator(多项式取模)
3.Miskcoo大佬:多项式的多点求值与快速插值
4.ZZQ大佬:多项式多点求值和插值
5.xyz32768大佬:[学习笔记]多项式的整除、取模、多点求值和插值及常系数线性递推
[等一下,这文章题目好像…]
同时,也用作学习资料供大家参考。