[学习笔记]多项式相关运算1(求逆,除法,取模,多点求值,快速插值)及应用

前言

突然发现自己好久都没写过博客了…赶紧来补一篇。
[其实自己写过几篇的…然而写到一半就咕掉了…]
相信总有一天会咕回来的
[回归正题]
最近学了有关多项式计算的一些知识。
于是蒟蒻完全进入了挂机状态。
作者用了两篇文章介绍了多项式全家桶的大部分成员[见标题]。
[学习笔记]多项式相关运算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_RdegR<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 degAdegB
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) A1(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)c11(modx), A ( x ) − 1 = c − 1 A(x)^{-1}=c^{-1} A(x)1=c1
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} x2n的逆元,假设我们现在已经知道 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}) x2n) ( 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}) x2n) ( 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}) x2n)
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}) x2n)
两边平方一下
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)+B2(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}) x2n)
可以理解为取模之后各项的系数都为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} x2n下只有 ⌈ 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=0iajaij,
i i i, i − j i-j ij必有一项小于 ⌈ 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)+B2(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)+B2(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)B2(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 nm
找到一个多项式 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 degDnm, d e g R < m deg_RdegR<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)=xnmD(x1)xmB(x1)+xnm+1xm1P(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)+xnm+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} xnm+1,将 P R ( x ) P^R(x) PR(x)消掉。
同时, D ( x ) D(x) D(x)的最高次项是 n − m n-m nm次,也不会对我们求解 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}) xnm+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)BR1(x)DR(x)(mod x n − m + 1 ) x^{n-m+1}) xnm+1)
我们只需求 B R − 1 ( x ) B^{R^{-1}}(x) BR1(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 nm
找到一个多项式 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 degDnm, d e g R < m deg_RdegR<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
#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,...,xn1}
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,...,x2n}
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]={x2n+1,x2n+2,x2n+3,...,xn1}
同时,我们必须能够把系数减半。
令点集 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=02n(xxi)
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+1n(xxi)
然而为什么我们要将多项式构造成这样呢?
容易发现当 x ∈ X [ 0 ] x\in X^{[0]} xX[0]时, P [ 0 ] = 0 P^{[0]}=0 P[0]=0,当 x ∈ X [ 1 ] x\in X^{[1]} xX[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]} xX[0]时, A ( x ) A(x) A(x) A [ 0 ] ( x ) A^{[0]}(x) A[0](x)取相同的值, x ∈ X [ 1 ] x\in X^{[1]} xX[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),...,(xn1,yn1)}
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),...,(x2n,y2n)}
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]={(x2n+1,y2n+1),(x2n+2,y2n+2),...,(xn1,yn1)}
令点集 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=02n(xxi)
考虑构造
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)yiA[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大佬:[学习笔记]多项式的整除、取模、多点求值和插值及常系数线性递推
[等一下,这文章题目好像…]
同时,也用作学习资料供大家参考。

你可能感兴趣的:(学习笔记)