芝士:多项式的瓜皮操作

本博客较长,建议分批次食用

前置芝士

lim

\(lim_{x\rightarrow y}\)表示x趋近于y,但x小于y,y是常数

求导&导数

当自变量的增量趋近于0时,因变量的增量和自变量之商的极限。

如果我们已知函数\(f(x)\),那么我们对其求导之后的函数称为\(f'(x)\)

也就是求极限\(lim_{\delta\rightarrow 0}f'(x)=\frac{f(x+\delta)-f(x)}{\delta}\)

导数就是\(lim_{\delta\rightarrow 0}f'(x)=\frac{f(x+\delta)-f(x)}{\delta}\)

有性质

\((f(g(x)))'=f'(g(x))*g'(x)\)

对数

\(a^n=b\),那么\(log_a^b=n\),我们称\(log\)为对数函数

自然常数

定义\(lim_{x\rightarrow+\infty}e=(1+\frac{1}{x})^x\)

自然对数

定义\(ln\space N=log_{e}^{N}\)

有性质

\(ln'(x)=\frac{1}{x}\)

泰勒展开

公式:

\(f(x)=\sum_{i=0}^{n}(\frac{f^{'(i个)}(x_0)}{i!}*(x-x_0)^i)+R_n(x)\)

\(x_0\)是个随机数

\(f^{'(i个)}\)表示\(f^{'(i-1个)}\)的导数

\(R_n(x)\)表示偏差

巨佬泰勒告诉我们当n足够大时,偏差就越小

也就是

\(f(x)=\sum_{i=0}^{\infty}\frac{f^{'(i个)}(x_0)}{i!}*(x-x_0)^i)\)

牛顿迭代

我们设\(r\)\(f(x)=0\)的根

公式为

\(x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)}\)

\(x_0\)是一个随机值

巨佬牛顿告诉我们,迭代次数越多,

\(x_n\)就越接近于\(r\)

不定积分

根据笔者粗略的理解

\(A\)为一个函数

就是\(A=\int A^{-1}\)

定积分

想象\(f\)这一个函数在坐标系上的样子

别介意图片马赛克一样的画质

如果我们想求\((x_1,y_1),(x_n,y_n)\)的曲线的面积

我们实际上可以将其分成许多个小的矩形,

不难想到,当\(\Delta x\)足够小时,标红区域的面积就会越来越接近正确答案

我们将其表示为

\(\int_{a}^{b}(f(x)*dx)\)

其中\(dx\)就为\(\Delta x\)

当然如果还不能理解的话,请看下面这一个公式

\(\int_{a}^{b}(f(x)*dx)=lim_{x\rightarrow+\infty}\sum_{i=1}^{n}(f(a+\frac{i}{n}(b-a))*\frac{b-a}{n})\)

你可以将其理解为将区间\([a,b]n\)等分

正题

多项式の乘

傅里叶说:这一项我包揽了

于是就有了FFT和改进的NTT

在此,笔者不要脸的链了个自己的博客

传送门

多项式の逆元

推导

给定\(A(x)\),求\(A^{-1}(x)\)满足

\(A(x)A^{-1}(x)\equiv1\pmod{x^n}\)

Tips:我们称\(A^{-1}\)\(A\)\(\pmod{x^n}\)意义下的逆元

注意此时的\(\pmod{x^n}\)为舍去次数\(\geq n\)的项数,只保留\(0\)\(n-1\)的项

还有一点很明显的是

\(n=1\)时,答案可以直接快速幂求出

虽然这是一个很明显的性质,但是它极其有用

我们假设已经求出了\(\pmod {x^n}\)意义下\(A\)的逆元\(B_0\),显然有

\(A(x)*B_0(x)\equiv 1\pmod{x^n}\)

我们想要的函数\(B\),满足

\(A(x)*B(x)\equiv 1\pmod {x^{2n}}\)

两式相减可以得到

\(A(x)*(B(x)-B_0(x))\equiv0 \pmod{x^n}\)

为什么是\(\pmod {x^n}\)呢?

我们这样考虑

我们设\(f(x)=A(x)*B(x)\)

那么\(f(x)\equiv1\pmod {x^n}\)

注意此时的\(f\)是对任意\(x\)都成立的

所以\(f\)的表达式一定是只有常数项的

我们继续考虑处理式子,两边同时除以\(A\)

则得到

\(B(x)-B_0(x)\equiv 0 \pmod{x^n}\)

你可以通过这个式子推出\(B和B_0\)前n-1项的一些性质

也就是前n-1项全部相等

因为我们要从\(x^n\rightarrow x^{2n}\),所以我们考虑平方一下

\((B(x)-B_0(x))^2\equiv0\pmod{x^{2n}}\)

为什么这里是\(2n\),就跟\(B和B_0\)前n-1项相等有关

将其展开

\(B(x)^2-2B(x)B_0(x)+B^2_0(x)\equiv 0\pmod{x^{2n}}\)

在乘一个\(A\),可以得到

\(B(x)-2*B_0(x)+A(x)*B_0^2(x)\equiv0\pmod{x^{2n}}\)

\(B(x)\equiv2*B_0(x)-A(x)*B_0^2(x)\pmod{x^{2n}}\)

\(B(x)\equiv B_0(x)*(2-A(x)*B_0(x))\pmod {x^{2n}}\)

在这里,因为\(B\)的次数超过\(x^{2n}\)就没有意义了

所以可以直接当作等于

代码

poly inv (const poly &f)//逆元
{
    int a[M]={};
    int b[M]={};
    poly g;
    g.push_back(inv(f[0]));
    for(int len=2;(len>>1)<f.size();len<<=1)
    {
        int lim=init(len<<1);
        int inv_len=inv(lim);
        memset(&a[len],0,len*SIZE);
        memset(&b[len],0,len*SIZE);
        for(int i=0;i<len;i++)
            a[i]=i<f.size()?f[i]:0;
        for(int i=0;i<len;i++)
            b[i]=i<g.size()?g[i]:0;
        ntt(a,lim);
        ntt(b,lim);
        for(int i=0;i<lim;i++)
            a[i]=(1ll*a[i]*b[i]%mod)*b[i]%mod;
        reverse(a+1,a+lim);
        ntt(a,lim);
        g.resize(len);
        for(int i=0;i<g.size();i++)
            g[i]=sub(add(g[i],g[i]),1ll*a[i]*inv_len%mod);
        g.resize(f.size());
    }
    g.resize(f.size());
    return g;
}

多项式の除法&多项式の取模

推导

给定\(n-1\)次多项式\(A(x)\)\(m-1\)次多项式\(B(x)\),求\(D(x)和R(x)\),满足

\(A(x)=B(x)*D(x)+R(x)\)

\(R\)的最高次项\(< m-1\)

同时满足

\(A(x)\equiv R(x)\pmod{B(x)}\)

我们定义反转操作

\(A^R(x)=x^{n-1}*A(\frac{1}{x})=\sum_{i=0}^{n-1}(a_{n-i-1}*x_i)\)

如果我们将\(\frac{1}{x}\)带入式子

\(A(\frac{1}{x})=D(\frac{1}{x})*B(\frac{1}{x})+R(\frac{1}{x})\)

再同乘一个\(x^{n-1}\),(因为我们要针对\(\frac{1}{x}\)

可得到

\(x^{n-1}A(\frac{1}{x})=x^{n-m}D(\frac{1}{x})x^{m+1}B(\frac{1}{x})+x^{n-m+1}x^{m-2}R(\frac{1}{x})\)

这个式子像什么?

不就是反转操作?

\(A^R(x)=D^R(x)B^R(x)+x^{n-m+1}R^R(x)\)

反转操作并不会改变\(D\)的最高次项,所以在\(\pmod {x^{n-m+1}}\)的意义下,我们就能消除\(R\)的影响

\(A^R(x)\equiv D^R(x)B^R(x)\pmod{x^{n-m+1}}\)

\(\frac{A^R(x)}{B^R(x)}\equiv D^R(x)\pmod{x^{n-m+1}}\)

由于\(D^R\)的最高次项就已经小于\(x^{n-m+1}\),所以可以写等于

注意是在\(\pmod{x^{x-m+1}}\)的意义下

代码

poly resize(poly f,int n)
{
    f.resize(n);
    return f;
}
void div (poly f,poly q,poly &g,poly &r)
{
    int lenq=q.size();
    reverse(f.begin(),f.end());
    reverse(q.begin(),q.end());
    poly _q=q;
    q=resize(q,f.size()-q.size()+1); 
    g=resize(f*inv(q),f.size()-lenq+1);
    reverse(g.begin(),g.end());
    reverse(f.begin(),f.end());
    q=_q;
    reverse(q.begin(),q.end());
    r=resize(f-q*g,lenq-1);
}

多项式の牛顿迭代法

推导

这是一种思想,并没有任何代码

已知\(g(f(x))=0\),同时\(g(x)\)已知,求\(f(x)\)

假设我们已经知道\(f(x)\)的前n项,\(f_0(x)\)

\(\begin{cases}f(x)\equiv f_0(x)&\pmod{x^n}\\g(f_0(x))\equiv0&\pmod{x^n}\end{cases}\)

之后我们大力泰勒展开

\(g(f(x))=\sum_{i=0}^{\infty}(\frac{g'^{i个}(f_0(x))}{i!}*(f(x)-f_0(x))^i)\)

之后我们根据\(f_0(x)\)的定义

\(g(f(x))\equiv g(f_0(x))+g'(f_0(x))(f(x)-f_0(x))\equiv0\pmod{x^{2n}}\)

解释一下为什么是\(x^{2n}\),我们从二次项看,\(f(x)-f_0(x)\)的前n项为0,所以平方之后前\(2n-2\)项就为0

注意这里是项,不是次

我们再考虑\(2n-1\)项,想想他的构成

\(\sum_{i=0}^{2n-1}a_ia_{2n-1-i}\),注意前n项为0

所以第\(2n-1\)项为0

之后

\(f(x)\equiv f_0(x)-\frac{g(f_0(x))}{g'(f_0(x))}\pmod{2^n}\)

多项式の开根

推导

给定多项式\(A(x)\),求\(B(x)\),满足

\(B^2(x)-A(x)\equiv 0\pmod{x^n}\)

\(B_0(x)\equiv B(x)\pmod {x^n}\)

大力迭代

\(B(x)\equiv B_0(x)-\frac{B_0^2(x)-A(x)}{2B_0(x)}\pmod {x^{2n}}\)

\(B(x)\equiv\frac{2*B_0^2(x)-B_0^2(x)+A(x)}{2B_0{x}}\pmod{2^n}\)

\(B(x)\equiv\frac{B_0^2(x)+A(x)}{2*B_0(x)}\pmod{2^n}\)

那么问题来了,当\(n=1\)时,怎么办呢?

cipolla不就完事了?

代码

namespace cipolla_namespace//二次剩余
{
    #define x first
    #define y second
    int t;
    int sqrt_w;
    pair<int,int> p;
    pair<int,int> operator * (const pair<int,int> &a,const pair<int,int> &b)
    {
        return make_pair((1ll*a.x*b.x+1ll*a.y*b.y%mod*sqrt_w)%mod,(1ll*a.x*b.y+1ll*a.y*b.x)%mod);
    }
    int cipolla(int x)
    {
        do
        {
            t=rand()%mod;
            sqrt_w=sub(1ll*t*t%mod,x);
        }
        while(qkpow(sqrt_w,(mod-1)>>1)!=mod-1);
        pair<int,int> s=make_pair(1,0);
        pair<int,int> a=make_pair(t,1);
        for(int b=(mod+1)>>1;b;b>>=1,a=a*a)
            if(b&1)
                s=s*a;
        return min(s.x,mod-s.x);
    }
    #undef x
    #undef y
}using cipolla_namespace::cipolla;
poly sqrt(const poly &f)//开根
{
    poly g;
    g.push_back(cipolla(f[0]));
    for(int len=2;( 
        g=resize(resize(resize(g*g,len)+f,len)*inv(resize(2*g,len)),len);
    g.resize(f.size());
    return g;
}

多项式のln

推导

对于一个多项式\(A(x)\),求\(ln(A(x))\pmod{x^n}\)

大力推导

\(ln(A(x))=\int (ln(A(x)))'=\int (ln'(A(x))*A'(x))=\int\frac{A'(x)}{A(x)}\pmod{x^n}\)

代码

poly deri(const poly &f)//求导
{
    poly g;
    for(int i=0;i<f.size()-1;i++)
        g.push_back(1ll*(i+1)*f[i+1]%mod);
    g.push_back(0);
    return g;
}
poly inte(poly f)//积分
{
    poly g;
    g.push_back(0);
    for(int i<f.size()-1;i++)
        g.push_back(1ll*inv(i+1)*f[i]%mod);
    return g;
}
poly ln(const poly &f)
{
    return inte(resize(deri(f)*inv(f),f.size()));
}

多项式のexp

推导

对于一个多项式\(A(x)\),求

\(e^{A(x)}\pmod {x^n}\)

可以假设\(B(x)\equiv e^{A(x)}\pmod{x^n}\)

\(ln(B(x))\equiv A(x)\pmod {x^n}\)

\(ln(B(x))-A(x)\equiv0\pmod{x^n}\)

再设\(B_0(x)\equiv B(x)\pmod{x^n}\)

大力牛顿迭代

\(B(x)\equiv B_0(x)-\frac{ln(B_0(x))-A(x)}{\frac{1}{B_0(x)}}\pmod{x^{2n}}\)

再将下面的倒上来

\(B(x)\equiv B_0(x)(1-ln(B_0(x))+A(x))\pmod{x^{2n}}\)

代码

poly exp(const poly &f)
{
    poly g;
    g.push_back(1);
    for(int len=2;(len>>1)<f.size();len<<=1)
        g=resize(g*(1-ln(resize(g,len))+resize(f,len)),len);
    g.resize(f.size());
    return g;
}

多项式のk次幂

推导

给定多项式\(F(x)\),和正整数k,求\(F^k(x)\),的前n项系数

大力快速幂

代码

poly qkpow(poly a,int b)
{
    int n=a.size();
    poly s;
    s.push_back(1);
    while(b)
    {
        if(b&1)
            s=resize(s*a,n);
        a=resize(a*a,n);
        b>>=1;
    }
    return s;
}

封装

笔者在此不要脸的再次链了个链接

虽然原题没有除法,但是笔者还是将除法封装了进去

挑战多项式

posted @ 2019-12-24 09:43  loney_s  阅读(338)  评论(0)    收藏  举报