多项式初步

多项式初步

自己写的

分治FFT/NTT Part1

给定序列 \(g_{1\dots n - 1}\),求序列 \(f_{0\dots n - 1}\)

其中 \(f_i=\sum_{j=1}^if_{i-j}g_j\),边界为 \(f_0=1\)

可以考虑cdq分治,对于f数组f[1,3|0,0]

假设左半边已经处理完,如何处理算左边对右边的贡献呢

提出[1,3,0,0](注意后面要清零),乘上g数组

然后把结果的后半加到f中

分治FFT/NTT Part2

\(\prod (1+x^{c_i}),\sum c_i=n\)

①.多项式求逆:

问题:

已知一个次数为\(n-1\)的多项式\(F(x)\),求一个多项式\(G(x)\)使得\(F(x)*G(x)\equiv 1\)(\(mod x^{n}\)),所有运算在模998244353意义下进行

怎么搞?

先进行一点分析:

如果\(F(x)\)只有一项,那么\(G(x)\)里也只有一项,就是\(F(x)\)里那项的逆元

那么如果我们已知一个多项式\(H(x)\)满足\(F(x)*H(x)\equiv 1\)(\(mod\) \(x^{\frac{n}{2}}\))

很显然,根据要求,同时有:\(F(x)*G(x)\equiv 1\)(\(mod\) \(x^{\frac{n}{2}}\))

下式减上式,得到:

\(F(x)(G(x)-H(x))\equiv 0\)(\(mod\) \(x^{\frac{n}{2}}\))

于是有

\(G(x)-H(x)\equiv 0\)(\(mod\) \(x^{\frac{n}{2}}\))

两边平方,可以得到:

\((G(x)-H(x))^{2}\equiv 0\) (\(mod\) \(x^{n}\))

展开,得:

\(G(x)^{2}+H(x)^{2}-2G(x)H(x)\equiv 0\)(\(mod\) \(x^{n}\))

两边乘一个\(F(x)\),有:

\(F(x)G(x)^{2}+F(x)H(x)^{2}-2F(x)G(x)H(x)\equiv 0\)(\(mod\) \(x^{n}\))

根据要求,\(F(x)G(x)\equiv 1\)(\(mod\) \(x^{n}\))

于是上式可以化简成:

\(G(x)+F(x)H(x)^{2}-2H(x)\equiv 0\)(\(mod\) \(x^{n}\))

移项,得到:

\(G(x)\equiv 2H(x)-F(x)H(x)^{2}\) (\(mod\) \(x^{n}\))

这样就可以倍增求解了

②.多项式带余除法:

多项式求逆是多项式除法的基础,如果你不会多项式求逆,请看这里

问题:已知两个多项式\(F(x)\)(次数为n),\(G(x)\)(次数为m),求两个多项式\(Q(x)\)\(R(x)\),满足\(F(x)=G(x)Q(x)+R(x)\),所有运算在模998244353意义下进行

推一发式子:

\(F(x)=G(x)Q(x)+R(x)\)

\(\frac{1}{x}\)替代\(x\),得到:

\(F(\frac{1}{x})=G(\frac{1}{x})Q(\frac{1}{x})+R(\frac{1}{x})\)

两边乘一个\(x^{n}\),得:

\(x^{n}F(\frac{1}{x})=x^{m}G(\frac{1}{x})x^{n-m}Q(\frac{1}{x})+x^{n}R(\frac{1}{x})\)

对表达式\(F(x)=G(x)Q(x)+R(x)\)进行分析,可以看到,若\(F(x)\)次数为n,\(G(x)\)次数为m,则\(Q(x)\)次数为\(n-m\)\(R(x)\)次数不超过m-1

那么对自变量先取逆再乘一个最高次数等价于构造一个系数与原多项式恰好相反的多项式!

也即若\(F(x)=\sum_{i=0}^{n}a_{i}x^{i}\),则\(x^{n}F(\frac{1}{x})=\sum_{i=0}^{n}a_{n-i}x^{i}\)

我们记\(x^{n}F(\frac{1}{x})=\sum_{i=0}^{n}a_{n-i}x^{i}=F^{T}(x)\)

那么上式可以简写成\(F^{T}(x)=G^{T}(x)Q^{T}(x)+R^{T}(x)\)

可以发现,\(R^{T}(x)\)这个多项式前\((n-m+1)\)项的系数均为0!

因此如果我们在\(mod\) \(x^{n-m+1}\)意义下,可以立刻得出这个等式:

\(F^{T}(x)\equiv G^{T}(x)Q^{T}(x)\)(\(mod\) \(x^{n-m+1}\))

那么移项即得:

\(Q^{T}(x)\equiv \frac{F^{T}(x)}{G^{T}(x)}\)(\(mod\) \(x^{n-m+1}\))

这样就求出了\(Q(x)\),然后基于原表达式,可得:

\(R(x)=F(x)-G(x)Q(x)\)

\(R(x)\)就算出来了

③.多项式开根:

问题:已知一个多项式\(F(x)\)次数为\(n-1\),求一个多项式\(G(x)\)使得\((G(x))^{2}\equiv F(x)\)(\(mod\) \(x^{n}\))

(保证常数项为\(1\)

仍然是推式子

首先,不难发现的是如果\(F(x)\)次数为0,那么\(G(x)=1\)

类似多项式求逆,我们倍增处理:

设已知\(H(x)^{2}\equiv F(x)\)(\(mod\) \(x^{\frac{n}{2}}\))

那么有\(H(x)^{2}-F(x)\equiv 0\)(\(mod\) \(x^{\frac{n}{2}}\))

两边平方,得:

\([H(x)^{2}-F(x)]^{2}\equiv 0\)(\(mod\) \(x^{n}\))

两边加上\(4H(x)^{2}F(x)\),得到:

\([H(x)^{2}+F(x)]^{2}\equiv 4H(x)^{2}F(x)\)(\(mod\) \(x^{n}\))

两边除掉\(4H(x)^{2}\),得:

\(\frac{[H(x)^{2}-F(x)]^{2}}{4H(x)^{2}}\equiv F(x)\)(\(mod\) \(x^{n}\))

可以发现左边是一个完全平方的形式,那么我们整理一下,得到:

\([\frac{H(x)^{2}-F(x)}{2H(x)}]^{2}\equiv F(x)\)(\(mod\) \(x^{n}\))

那么我们所求的\(G(x)\)不就出来了嘛

\(G(x)=\frac{H(x)^{2}-F(x)}{2H(x)}\)

用类似多项式求逆的方法递归求解即可

④.多项式对数:

问题:已知一个次数为\(n-1\)的多项式\(F(x)\),求一个多项式\(G(x)\)使得\(G(x)\equiv ln(F(x))\)(\(mod\) \(x^{n}\))

(保证\(F(x)\)常数项为1)

这个比较简单:

两边求导,得:

\(G^{'}(x)\equiv \frac{F^{'}(x)}{F(x)}\)(\(mod\) \(x^{n}\))

右侧都已知,直接多项式求逆计算出来即可

然后两边做不定积分,本来会有一个常数项,但是考虑到\(ln(F(0))=ln1=0=C\),因此常数项直接为0即可

⑤.多项式exp:

问题:已知一个多项式\(F(x)\)次数为\(n-1\),求一个多项式\(G(x)\)满足\(G(x)\equiv e^{F(x)}\)(\(mod\) \(x^{n}\))

保证\(F(x)\)常数项为\(0\)

好像有点困难...

首先有一个基础知识:

我们可以用牛顿迭代求出一个多项式的多项式零点

也即已知一个多项式\(F(x)\),可以利用牛顿迭代求出一个多项式\(G(x)\)满足\(F(G(x))\equiv 0\)(\(mod\) \(x^{n}\))

为什么我们要知道这件事情?

假设我们已知了\(G_{0}(x)\equiv e^{F(x)}\)(\(mod\) \(x^{\frac{n}{2}}\)

那么我们需要求出的就是\(G_{0}\)\(G(x)\)的关系

首先,根据牛顿迭代公式,可得:
\(G(x)=G_{0}(x)-\frac{F(G_{0}(x)}{F^{'}(G_{0}(x))}\)

(关于这个公式的来历,我在最下面有一个感性理解的部分)

但是这个嵌套的东西还是很闹心

那么我们从另一个方向再进行一些推导:

已知\(G(x)\equiv e^{F(x)}\)(\(mod\) \(x^{n}\))

那么两边取对数

\(lnG(x)-F(x)\equiv 0\)(\(mod\) \(x^{n}\))

\(H(G(x))\equiv lnA(x)-B(x)\equiv 0\) (\(mod\) \(x^{n}\))

那么求导即得到\(H^{'}(G_{0}(x))\equiv G_{0}^{-1}(x)\)

那么再回到上面的表达式:

\(G(x)=G_{0}(x)-\frac{H(G_{0}(x)}{H^{'}(G_{0}(x))}\)

最后整理一遍,得到:

\(G(x)\equiv G_{0}(x)[1-lnG_{0}(x)+F(x)]\)(\(mod\) \(x^{n}\))

这样就可以倍增去求了

关于牛顿迭代:

假设我们要求一个函数\(f(x)\)的零点,那么我们不妨假设这个零点是\(x_{0}\),然后将\(f(x)\)\(x=x_{0}\)处求导得\(f^{'}(x_{0})\),计算出在这一点的切线

\(y=f^{'}(x_{0})(x-x_{0})+f(x_{0})\)

接下来求出该切线与\(x\)轴交点横坐标\(x_{1}\),那么\(x_{1}\)的精度就比\(x_{0}\)的精度好一倍(大概是这个意思吧)

我们计算出\(x_{1}\)的表达式,可以发现\(x_{1}=x_{0}-\frac{f(x_{0})}{f^{'}(x_{0})}\),那么这就是一个递推关系式,将未知数\(x\)换为多项式\(G(x)\)即得回上式

⑥.多项式快速幂:

问题:已知一个次数为\(n-1\)的多项式\(F(x)\),求一个多项式\(G(x)\)满足\(G(x)\equiv F(x)^{k}\)

这个...你需要多项式exp

直接推一发式子就可以了:

\(G(x)\equiv F(x)^{k}\)

\(G(x)\equiv e^{lnF(x)^{k}}\)

\(G(x)\equiv e^{klnF(x)}\)

这样写个多项式ln和多项式exp就可以了

模板

基础操作

#include<bits/stdc++.h>
#define Fu(i,a,b) for(register int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define mod 998244353 
using namespace std;
int ksm(int a,int k){
	int ans=1;
	while(k){
		if(k&1) ans=1ll*ans*a%mod;
		a=1ll*a*a%mod,k>>=1;
	}return ans;
}
const int N=4*1e5+5;
int rev[N],g=3,gi=ksm(3,mod-2);
void ntt(int n,int *a,int inv){
	Fu(i,0,n) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int len=1;len<n;len<<=1){
		int wn=ksm(inv?g:gi,(mod-1)/(len<<1));
		for(int i=0;i<n;i+=len<<1){
			int w0=1;
			Fu(j,0,len-1){
				int x=a[i+j],y=1ll*w0*a[i+j+len]%mod;
				a[i+j]=(x+y)%mod,a[i+j+len]=(x-y+mod)%mod,w0=1ll*w0*wn%mod;
			}
		}
	}
}
int c[N];
void mul(int *a,int *b,int n){
	int len=1,bit=0;
	while(len<2*n) len<<=1,bit++;
	Fu(i,0,len) rev[i]=(rev[i>>1]>>1)|((i&1)<<bit-1),c[i]=i<n?b[i]:0;
	ntt(len,a,1),ntt(len,c,1);
	Fu(i,0,len) a[i]=1ll*a[i]*c[i]%mod;
	ntt(len,a,0);
	Fu(i,0,len) a[i]=i<n?1ll*a[i]*ksm(len,mod-2)%mod:0;
}
void inv(int *a,int *b,int n){
	if(n==1){
		a[0]=ksm(b[0],mod-2);
		return ;
	}inv(a,b,n+1>>1);
	int len=1,bit=0;
	while(len<2*n) len<<=1,bit++;
	Fu(i,0,len) rev[i]=(rev[i>>1]>>1)|((i&1)<<bit-1),c[i]=i<n?b[i]:0;
	ntt(len,a,1),ntt(len,c,1);
	Fu(i,0,len) a[i]=1ll*a[i]*(2ll-1ll*a[i]*c[i]%mod+mod)%mod;
	ntt(len,a,0);
	Fu(i,0,len) a[i]=i<n?1ll*a[i]*ksm(len,mod-2)%mod:0;
}
int inv2=ksm(2,mod-2),d[N];
void sqr(int *a,int *b,int n){
	if(n==1){
		a[0]=1;
		return ;
	}sqr(a,b,n+1>>1);
	Fu(i,0,n) d[i]=0;
	inv(d,a,n),mul(a,a,n);
	Fu(i,0,n) a[i]=1ll*inv2*(a[i]+b[i])%mod; 
	mul(a,d,n);
}
void ln(int *a,int *b,int n){
	memset(d,0,sizeof(d));
	inv(d,b,n);
	Fu(i,1,n-1) a[i-1]=1ll*b[i]*i%mod;
	a[n-1]=0;
	mul(a,d,n);
	Fd(i,n-1,1) a[i]=1ll*a[i-1]*ksm(i,mod-2)%mod;
	a[0]=0;
}
int e[N];
void exp(int *a,int *b,int n){
	if(n==1){
		a[0]=1;
		return ;
	}exp(a,b,n+1>>1); 
	ln(e,a,n);
	Fu(i,0,n-1) e[i]=(b[i]-e[i]+mod)%mod;
	e[0]++;
	mul(a,e,n);
}
int n,m,a[N],b[N];
int main(){
	return 0;
}

MTT

四次FFT版

#include<bits/stdc++.h>
#define Fu(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define lim 32000
using namespace std;
const long double pi=acos(-1.0);
struct cp{
	long double x,y;
	cp operator + (const cp& p){return (cp){x+p.x,y+p.y};}
	cp operator - (const cp& p){return (cp){x-p.x,y-p.y};}
	cp operator * (const cp& p){return (cp){x*p.x-y*p.y,x*p.y+y*p.x};}
}P1[400005],P2[400005],Q[400005];
int rev[400005];
long double ccos[400005],ssin[400005];
void fft(int n,cp *a,int inv){
	Fu(i,0,n) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int mid=1;mid<n;mid<<=1){
		cp wn=(cp){ccos[mid],inv*ssin[mid]};
		for(int i=0;i<n;i+=mid<<1){
			cp w0=(cp){1,0};
			Fu(j,0,mid-1){
				cp x=a[i+j],y=w0*a[i+j+mid];
				a[i+j]=x+y,a[i+j+mid]=x-y,w0=w0*wn;
			}
		}
	}
}
void mul(int *ans,int *a,int *b,int n,int m,int p){
	Fu(i,0,n) P1[i]=(cp){a[i]/lim,a[i]%lim};
	Fu(i,0,m) Q[i]=(cp){b[i]/lim,b[i]%lim};
	int len=1,bit=0;
	while(len<=n+m+1) len<<=1,bit++,ccos[len]=cos(pi/len),ssin[len]=sin(pi/len);
	Fu(i,0,len) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
	fft(len,P1,1),fft(len,Q,1);
	Fu(i,0,len-1) P2[i]=P1[i?len-i:0],P2[i].y*=-1;
	Fu(i,0,len) Q[i].x/=len,Q[i].y/=len;
	Fu(i,0,len) P1[i]=P1[i]*Q[i],P2[i]=P2[i]*Q[i];
	fft(len,P1,-1),fft(len,P2,-1);
	Fu(i,0,n+m+1){
		long long a1b1,a1b2,a2b1,a2b2;
		a1b1=(long long)floor((P1[i].x+P2[i].x)/2+0.49)%p;
		a1b2=(long long)floor((P1[i].y+P2[i].y)/2+0.49)%p;
		a2b1=((long long)floor(P1[i].y+0.49)-a1b2)%p;
		a2b2=((long long)floor(P2[i].x+0.49)-a1b1)%p;
		ans[i]=((a1b1*lim+(a1b2+a2b1))*lim+a2b2)%p;
		ans[i]=(ans[i]+p)%p;
	}
} 
int n,m,p,a[100005],b[100005],ans[200005];
int main(){
	scanf("%d%d%d",&n,&m,&p);
	Fu(i,0,n) scanf("%d",&a[i]);
	Fu(i,0,m) scanf("%d",&b[i]);
	mul(ans,a,b,n,m,p);
	Fu(i,0,n+m) printf("%d ",ans[i]);
	return 0;
}

posted @ 2024-02-23 22:20  zhy_learn  阅读(2)  评论(0编辑  收藏  举报