突然发现自己好久都没写过博客了…赶紧来补一篇。
[其实自己写过几篇的…然而写到一半就咕掉了…]
相信总有一天会咕回来的
[回归正题]
最近学了有关多项式计算的一些知识。
于是蒟蒻完全进入了挂机状态。
作者用了两篇文章介绍了多项式全家桶的大部分成员[见标题]。
[学习笔记]多项式相关运算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
已知一个多项式 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:[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
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
有了上一节的计算,求 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
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
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:我放弃挣扎了
关于题解…它已经鸽掉了…
感谢以下dalao的帮助:
1.yoyoball大佬:【learning】多项式相关(求逆、开根、除法、取模)
2.CaptainChen学长:【CodeChef RNG】Random Number Generator(多项式取模)
3.Miskcoo大佬:多项式的多点求值与快速插值
4.ZZQ大佬:多项式多点求值和插值
5.xyz32768大佬:[学习笔记]多项式的整除、取模、多点求值和插值及常系数线性递推
[等一下,这文章题目好像…]
同时,也用作学习资料供大家参考。