多项式

形如 \(f(x)=a_0x^0+a_1x^1+a_2x^2+ \dots +a_{n-1}x^{n-1}\)

点值表示法:通过代入 \(n\) 个不同的值 \(x_0,x_1 \dots x_{n-1}\)\(f(x)\) 中,得到 \(y_0,y_1...y_{n-1}\),用 \((x_0,y_0),(x_1,y_1) \dots (x_{n-1},y_{n-1})\) 即可表示这个多项式。

若用点值表示法,并且两个多项式的 \(x\) 对应相等,那么将 \(y\) 对应相乘,即可 \(O(n)\) 的得到它们乘积的点值表示法。

从多项式的系数表示向点值表示的转化,称为求值,从多项式的点值表示向系数表示的转化,称为插值。

FFT

快速傅里叶变换可以做到 \(O(n\log n)\) 进行求值和插值。

复数相乘,模长相乘,幅角相加。

\[\large (a+bi)×(c+di)=(ac-bd)+(bc+ad)i \]

在复平面上,以原点为圆心,\(1\) 为半径作圆,所得的圆叫单位圆。以圆点为起点,圆的 \(n\) 等分点为终点,做 \(n\) 个向量,设幅角为正且最小的向量对应的复数为 \(ω_n\),称为 \(n\) 次单位根。

\[\large\begin{aligned} &ω_n^k=\cos\frac{2πk}{n}+i\sin\frac{2πk}{n} \\ &ω_{2n}^{2k}=ω_n^k \\ &ω_{n}^{k+\frac{n}{2}}=-ω_n^k \end{aligned} \]

\[\large f(x)=a_0x^0+a_1x^1+a_2x^2+a_3x^3+ \dots +a_{n-2}x^{n-2}+a_{n-1}x^{n-1} \]

进行奇偶分类得:

\[\large\begin{aligned} f1(x)=a_0+a_2x^1+a_4x^2+a_6x^3+ \dots +a_{n-2}x^{\frac{n}{2}-1}\\ f2(x)=a_1+a_3x^1+a_5x^2+a_7x^3+ \dots +a_{n-1}x^{\frac{n}{2}-1} \end{aligned} \]

\(f(x)=f1(x^2)+xf2(x^2)\)

\(k<\frac{n}{2}\),代入 \(ω_n^k\) 得:

\[\large f(ω_n^k)=f1(ω_n^{2k})+ω_n^kf2(ω_n^{2k}) \]

再代入 \(ω_n^{k+\frac{n}{2}}\)

\[\large f(ω_n^{k+\frac{n}{2}})=f1(ω_n^{2k})-ω_n^kf2(ω_n^{2k}) \]

通过这两个式子,我们就可以进行递归求解了,但因递归常数过大,我们通过迭代来实现。

我们将原多项式系数重新排序,将每个系数都在它递归的最后位置,就可以用迭代来代替递归实现。

发现原来在第 \(a\) 个位置的系数,在递归的最后位置为 \(a\) 的二进制反转以后的数。

这称为蝴蝶操作。

代入 \(ω_n^{-k}\),可再次求得一系列值,将其除以 \(n\) 即可求得多项式相乘后的系数表示。

struct Complex
{
	double x,y;
	Complex(double a=0,double b=0)
	{
		x=a,y=b;
	}
}f[maxn],g[maxn];
Complex operator +(const Complex &a,const Complex &b)
{
	return Complex(a.x+b.x,a.y+b.y);
}
Complex operator -(const Complex &a,const Complex &b)
{
	return Complex(a.x-b.x,a.y-b.y);
}
Complex operator *(const Complex &a,const Complex &b)
{
	return Complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
int calc(int n)
{
    int lim=1;
    while(lim<=n) lim<<=1;
    for(int i=0;i<lim;++i)
        rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
    return lim;
}
void FFT(Complex *a,int lim,int type)
{
	for(int i=0;i<lim;++i)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);
	for(int len=1;len<lim;len<<=1)
	{
		Complex T(cos(Pi/len),type*sin(Pi/len));
		for(int i=0;i<lim;i+=len<<1)
		{
			Complex t(1,0);
			for(int j=i;j<i+len;++j,t=t*T)
			{
				Complex x=a[j],y=t*a[j+len];
				a[j]=x+y,a[j+len]=x-y;
			}
		}
	}
    if(type==1) return;
    for(int i=0;i<lim;++i) a[i].x=a[i].x/lim+0.5;
}
void mul(Complex *f,Complex *g)
{
    int lim=calc(n+m);
    FFT(f,lim,1),FFT(g,lim,1);
    for(int i=0;i<lim;++i) f[i]=f[i]*g[i];
    FFT(f,lim,-1);
}

NTT

\(a,p\) 互素,且 \(p>1\),对于 \(a^n \equiv 1 \pmod{m}\) 最小的 \(n\),称为 \(a\)\(p\) 的阶,记作 \(δ_p(a)\)

\(p\) 是正整数,\(a\) 是整数,若 \(δ_p(a)=\varphi(p)\),则称 \(a\) 为模 \(p\) 的一个原根。

原根个数不唯一。

\(P\) 为素数,设 \(G\)\(P\) 的原根,那么 \(G^i \bmod P,(i<G<P,0<i<P)\) 的结果两两不同。

模数有 \(998244353,1004535809,469762049\),原根都为 \(3\)

\[\large ω_n \equiv g^{\frac{p-1}{n}} \pmod{p} \]

int calc(int n)
{
    int lim=1;
    while(lim<=n) lim<<=1;
    for(int i=0;i<lim;++i)
        rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
    return lim;
}
void NTT(ll *a,int lim,int type)
{
    for(int i=0;i<lim;++i)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);
    for(int len=1;len<lim;len<<=1)
    {
        ll wn=qp(type==1?G:Gi,(P-1)/(len<<1));
        for(int i=0;i<lim;i+=len<<1)
        {
            ll w=1;
            for(int j=i;j<i+len;++j,w=w*wn%P)
            {
                ll x=a[j],y=w*a[j+len]%P;
                a[j]=(x+y)%P,a[j+len]=(x-y+P)%P;
            }
        }
    }
    if(type==1) return;
    ll inv=qp(lim,P-2);
    for(int i=0;i<lim;++i) a[i]=a[i]*inv%P;
}
void mul(ll *f,ll *g)
{
    int lim=calc(n+m);
    NTT(f,lim,1),NTT(g,lim,1);
    for(int i=0;i<lim;++i) f[i]=f[i]*g[i]%P;
    NTT(f,lim,-1);
}

多项式求导和积分

\[\large\begin{aligned} \left [ x^i \right ] {f}'(x) &= (i+1) \left [ x^{i+1} \right ]f(x) \\ \left [ x^i \right ] \int f(x) &= \frac{1}{i} \left [ x^{i-1} \right ]f(x) \end{aligned} \]

多项式牛顿迭代

已知 \(f(x)\),求 \(g(x)\),满足 \(f(g(x)) \equiv 0 \pmod{x^n}\)

设已经求得 \(g_0(x)\),满足 \(g_0(x) \equiv g(x) \pmod{x^n}\),由泰勒展开得:

\[\large\begin{aligned} 0 &= f(g(x)) \\ &= f(g_0(x)) + {f}'(g_0(x))(g(x)-g_0(x)) + \frac{{f}''(g_0(x))}{2}(g(x)-g_0(x))^2+ \dots \\ & \equiv f(g_0(x)) + {f}'(g_0(x))(g(x)-g_0(x)) \pmod{x^{2n}} \end{aligned} \]

得:

\[\large g(x) \equiv g_0(x) - \frac{f(g_0(x))}{{f}'(g_0(x))} \pmod{x^{2n}} \]

多项式求逆

\[\large f(x)g(x) \equiv 1 \pmod{x^n} \]

\(g(x)\)\(f(x)\) 的逆元,模 \(x^n\) 的意义是将次数大于等于 \(n\) 的项都忽略掉。

设已经求得满足

\[\large f(x)g(x) \equiv 1 \pmod{x^n} \]

\(g(x)\),得:

\[\large \begin{aligned} f(x)g(x) - 1 &\equiv 0 \pmod{x^n} \\ (f(x)g(x)-1)^2 &\equiv 0 \pmod{x^{2n}} \\ f(x)(2g(x)-g^2(x)f(x)) &\equiv 1 \pmod{x^{2n}} \end{aligned} \]

本质为牛顿迭代

\(g(x) = f^{-1}(x)\)\(h(g(x)) = g^{-1}(x) - f(x) =0\)

设已经求得 \(g_0(x)\),满足 \(g_0(x) \equiv g(x) \pmod{x^n}\),得:

\[\large\begin{aligned} g(x) & \equiv g_0(x) - \frac{h(g_0(x))}{{h}'(g_0(x))} \pmod{x^{2n}} \\ & \equiv g_0(x) - \frac{g^{-1}_0(x)-f(x)}{-g^{-2}_0(x)} \pmod{x^{2n}} \\ & \equiv 2g_0(x) - g_0^2(x)f(x) \pmod{x^{2n}} \end{aligned} \]

多项式对数函数

\[\large\begin{aligned} g(x) &= \ln f(x) \\ {g}' (x) &= \frac{{f}' (x)}{f(x)} \end{aligned} \]

多项式指数函数

\(g(x) = e^{f(x)}\)\(h(g(x)) = \ln g(x) - f(x) =0\)

设已经求得 \(g_0(x)\),满足 \(g_0(x) \equiv g(x) \pmod{x^n}\),得:

\[\large\begin{aligned} g(x) & \equiv g_0(x) - \frac{h(g_0(x))}{{h}'(g_0(x))} \pmod{x^{2n}} \\ & \equiv g_0(x) - \frac{\ln g_0(x)-f(x)}{g^{-1}_0(x)} \pmod{x^{2n}} \\ & \equiv g_0(x)(1 - \ln g_0(x)+f(x)) \pmod{x^{2n}} \end{aligned} \]

多项式幂函数

\[\large f^k(x) = e^{k \ln f(x)} \]

多项式开根

可以直接使用多项式幂函数求解,也可以用牛顿迭代求解。

\(g^2(x) = f(x)\)\(h(g(x)) = g^2(x) - f(x) =0\)

设已经求得 \(g_0(x)\),满足 \(g_0(x) \equiv g(x) \pmod{x^n}\),得:

\[\large\begin{aligned} g(x) & \equiv g_0(x) - \frac{h(g_0(x))}{{h}'(g_0(x))} \pmod{x^{2n}} \\ & \equiv g_0(x) - \frac{ g_0^2(x)-f(x)}{2g_0(x)} \pmod{x^{2n}} \\ & \equiv \frac{ g_0^2(x)+f(x)}{2g_0(x)} \pmod{x^{2n}} \end{aligned} \]

多项式除法

已知 \(n\) 次多项式 \(f(x)\)\(m\) 次多项式 \(g(x)\),求满足 \(f(x) \equiv g(x)h(x)+t(x) \pmod{x^{n+1}}\)\(n-m\) 次多项式 \(h(x)\) 和次数小于 \(m\) 的多项式 \(t(x)\)

\(f_r(x)\) 为多项式 \(f(x)\) 系数翻转得到的多项式,得 \(f_r(x) = x^nf(x^{-1})\)

\[\large\begin{aligned} f(x) &\equiv g(x)h(x)+t(x) \pmod{x^{n+1}} \\ x^n f(x^{-1}) &\equiv x^m g(x^{-1}) x^{n-m} h(x^{-1}) + x^n t(x^{-1}) \pmod{x^{n+1}} \\ f_r(x) &\equiv g_r(x)h_r(x) + x^{n-m+1} t_r(x) \pmod{x^{n+1}} \\ f_r(x) &\equiv g_r(x)h_r(x) \pmod{x^{n-m+1}} \\ h_r(x) &\equiv f_r^{-1}(x)g_r(x) \pmod{x^{n-m+1}} \end{aligned} \]

求出 \(h(x)\) 后,得 \(t(x) \equiv f(x) - g(x)h(x) \pmod{x^m}\)

递推

多项式操作的 \(O(n^2)\) 递推。

多项式求逆

\[\large\begin{aligned} F(x)G(x)&=1 \\ \sum_{i=0}^nf_ig_{n-i}&=[n=0] \\ g_n&=-\frac{1}{f_0}\sum_{i=1}^nf_ig_{n-i} \end{aligned} \]

边界为 \(g_0=inv(f_0)\)

多项式对数函数

\[\large\begin{aligned} G(x)&=\ln F(x) \\ {G}'(x)&=\frac{{F}'(x)}{F(x)} \\ \sum_{i=0}^n(i+1)g_{i+1}f_{n-i}&=(n+1)f_{n+1} \\ (n+1)g_{n+1}f_0&=(n+1)f_{n+1}-\sum_{i=0}^{n-1}(i+1)g_{i+1}f_{n-i} \\ g_{n+1}&=\frac{1}{(n+1)f_0}\left( (n+1)f_{n+1}-\sum_{i=0}^{n-1}(i+1)g_{i+1}f_{n-i} \right)\\ g_n&=\frac{1}{nf_0}\left( nf_n-\sum_{i=1}^{n-1}ig_if_{n-i} \right)\\ \end{aligned} \]

多项式指数函数

\[\large\begin{aligned} G(x)&=e^{F(x)} \\ {G}'(x)&=G(x){F}'(x) \\ (n+1)g_{n+1}&=\sum_{i=0}^n(i+1)f_{i+1}g_{n-i} \\ g_{n+1}&=\frac{1}{(n+1)}\sum_{i=0}^n(i+1)f_{i+1}g_{n-i} \\ g_n&=\frac{1}{n}\sum_{i=1}^nif_ig_{n-i} \\ \end{aligned} \]

边界为 \(g_0=1\)

模板

void Inv(int deg,ll *a,ll *b)
{
    static ll t[maxn];
    if(deg==1)
    {
        b[0]=qp(a[0],P-2);
        return;
    }
    Inv((deg+1)>>1,a,b);
    int lim=calc(deg<<1);
    for(int i=0;i<deg;++i) t[i]=a[i];
    for(int i=deg;i<lim;++i) t[i]=b[i]=0;
    NTT(t,lim,1),NTT(b,lim,1);
    for(int i=0;i<lim;++i) b[i]=b[i]*(2-t[i]*b[i]%P+P)%P;
    NTT(b,lim,-1);
    for(int i=deg;i<lim;++i) b[i]=0;
}
void Ln(int deg,ll *a,ll *b)
{
    static ll inva[maxn],dera[maxn];
    Inv(deg,a,inva);
    for(int i=0;i<deg-1;++i) dera[i]=a[i+1]*(i+1)%P;
    dera[deg-1]=0;
    int lim=calc(deg<<1);
    for(int i=deg;i<lim;++i) dera[i]=inva[i]=0;
    NTT(dera,lim,1),NTT(inva,lim,1);
    for(int i=0;i<lim;++i) b[i]=dera[i]*inva[i]%P;
    NTT(b,lim,-1);
    for(int i=deg-1;i>=1;--i) b[i]=b[i-1]*qp(i,P-2)%P;
    b[0]=0;
    for(int i=deg;i<lim;++i) b[i]=0;
}
void Exp(int deg,ll *a,ll *b)
{
    static ll t[maxn],lnb[maxn];
    if(deg==1)
    {
        b[0]=1;
        return;
    }
    Exp((deg+1)>>1,a,b),Ln(deg,b,lnb);
    int lim=calc(deg<<1);
    for(int i=0;i<deg;++i) t[i]=(a[i]-lnb[i]+P)%P;
    for(int i=deg;i<lim;++i) t[i]=b[i]=0;
    NTT(t,lim,1),NTT(b,lim,1);
    for(int i=0;i<lim;++i) b[i]=b[i]*(1+t[i])%P;
    NTT(b,lim,-1);
    for(int i=deg;i<lim;++i) b[i]=0;
}
void Pow(int deg,ll *a,ll *b,ll k)
{
    static ll lna[maxn];
    Ln(deg,a,lna);
    for(int i=0;i<deg;++i) lna[i]=lna[i]*k%P;
    Exp(deg,lna,b);
}
void Sqrt(int deg,ll *a,ll *b)
{
    static ll t[maxn],invb[maxn];
    if(deg==1)
    {
        b[0]=1;
        return;
    }
    Sqrt((deg+1)>>1,a,b);
    int lim=calc(deg<<1);
    for(int i=0;i<deg;++i) t[i]=2*b[i]%P,invb[i]=0;
    for(int i=deg;i<lim;++i) t[i]=invb[i]=0;
    Inv(deg,t,invb);
    for(int i=0;i<deg;++i) t[i]=a[i];
    for(int i=deg;i<lim;++i) t[i]=b[i]=0;
    NTT(t,lim,1),NTT(b,lim,1),NTT(invb,lim,1);
    for(int i=0;i<lim;++i) b[i]=(b[i]*b[i]%P+t[i])%P*invb[i]%P;
    NTT(b,lim,-1);
    for(int i=deg;i<lim;++i) b[i]=0;
}
posted @ 2020-01-22 20:32  lhm_liu  阅读(565)  评论(0编辑  收藏  举报