多项式板子

一、数组版本

数组版本和 poly 版本都只涵盖目录中第 \(4\sim 10\) 部分。

namespace Poly
{
    int p[maxn],q[maxn],r[maxn],w[maxn];
    int inum[maxn];
    int qpow(int a,int k)
    {
        int res=1;
        for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
        return res;
    }
    int add(int x,int y)
    {
        if((x+=y)>=mod) x-=mod;
        return x;
    }
    int dec(int x,int y)
    {
        if((x-=y)<0) x+=mod;
        return x;
    }
    int extend(int n)
    {
        return 1<<(__lg(n-1)+1);
    }
    void get_r(int n)
    {
        for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|(i&1?n>>1:0);
    }
    void init(int n=maxn)
    {
        for(int k=2,m=1;k<=n;k<<=1,m<<=1)
        {
            w[m]=1;
            for(int i=m+1,x=qpow(3,(mod-1)/k);i<k;i++) w[i]=1ll*w[i-1]*x%mod;
        }
        for(int i=1;i<n;i++) inum[i]=qpow(i,mod-2);
    }
    void print(int *a,int n)
    {
        for(int i=0;i<n;i++) printf("%d%c",a[i]," \n"[i==n-1]);
    }
    void ntt(int *a,int n,int op)
    {
        for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]);
        for(int k=2,m=1;k<=n;k<<=1,m<<=1)
            for(int i=0;i<n;i+=k)
                for(int j=i,*x=w+m;j<i+m;j++,x++)
                {
                    int v=1ll*a[j+m]**x%mod;
                    a[j+m]=dec(a[j],v),a[j]=add(a[j],v);
                }
        if(op==-1)
        {
            reverse(a+1,a+n);
            int v=qpow(n,mod-2);
            for(int i=0;i<n;i++) a[i]=1ll*a[i]*v%mod;
        }
    }
    void mul(int *a,int *b,int n,int m)
    {
        int len=extend(n+m-1);
        for(int i=0;i<len;i++) p[i]=i<n?a[i]:0,q[i]=i<m?b[i]:0;
        get_r(len),ntt(p,len,1),ntt(q,len,1);
        for(int i=0;i<len;i++) a[i]=1ll*p[i]*q[i]%mod;
        ntt(a,len,-1);
    }
    void inv(int *a,int *b,int n)
    {
        static int c[maxn],d[maxn];
        n=extend(n),memset(b,0,4*n),b[0]=qpow(a[0],mod-2);
        for(int k=2;k<=n;k<<=1)
        {
            for(int i=0;i<k<<1;i++) c[i]=i<k?a[i]:0,d[i]=i<k>>1?b[i]:0;
            get_r(k),ntt(d,k,1);
            for(int i=0;i<k;i++) d[i]=1ll*d[i]*d[i]%mod;
            ntt(d,k,-1),get_r(k<<1),ntt(c,k<<1,1),ntt(d,k<<1,1);
            for(int i=0;i<k<<1;i++) c[i]=1ll*c[i]*d[i]%mod;
            ntt(c,k<<1,-1);
            for(int i=0;i<k;i++) b[i]=(2ll*b[i]-c[i]+mod)%mod;
        }
    }
    void diff(int *a,int *b,int n)
    {
        for(int i=1;i<n;i++) b[i-1]=1ll*i*a[i]%mod;
        b[n-1]=0;
    }
    void integ(int *a,int *b,int n)
    {
        for(int i=1;i<n;i++) b[i]=1ll*inum[i]*a[i-1]%mod;
        b[0]=0;
    }
    void ln(int *a,int *b,int n)
    {
        static int c[maxn],d[maxn];
        assert(a[0]==1);
        n=extend(n),inv(a,c,n),diff(a,d,n),mul(c,d,n,n),integ(c,b,n);
    }
    void exp(int *a,int *b,int n)
    {
        static int c[maxn];
        assert(a[0]==0);
        n=extend(n),memset(b,0,4*n),memset(c,0,4*n),b[0]=1;
        for(int k=2;k<=n;k<<=1)
        {
            ln(b,c,k);
            for(int i=0;i<k;i++) c[i]=dec(a[i],c[i]);
            c[0]++,mul(b,c,k,k);
        }
    }
    void sqrt(int *a,int *b,int n)
    {
        static const int inv2=(mod+1)>>1;
        static int c[maxn],d[maxn];
        assert(a[0]==1);
        n=extend(n),memset(b,0,4*n),b[0]=1;
        for(int k=2;k<=n;k<<=1)
        {
            memcpy(c,a,4*k),inv(b,d,k),mul(c,d,k,k);
            for(int i=0;i<k;i++) b[i]=1ll*(b[i]+c[i])*inv2%mod;
        }
    }
    /** enough for k<mod
    void pow(int *a,int *b,int n,int k)
    {
        static int c[maxn];
        memcpy(c,a,4*n),memset(b,0,4*n),b[0]=1;
        for(;k;mul(c,c,n,n),k>>=1) if(k&1) mul(b,c,n,n);
    }
    **/
    void pow(int *a,int *b,int n,string k)
    {
        static int c[maxn],d[maxn];
        int u=0,k1=0,k2=0;
        memset(b,0,4*n);
        for(int i=0;i<k.size();i++) k1=(10ll*k1+k[i]-'0')%mod,k2=(10ll*k2+k[i]-'0')%(mod-1);
        for(u=0;u<n&&!a[u];u++) ;
        if((u&&k.size()>=5)||1ll*u*k1>=n) return ;
        for(int i=u,x=qpow(a[u],mod-2);i<n;i++) c[i-u]=1ll*a[i]*x%mod;
        ln(c,d,n-u);
        for(int i=0;i<n-u;i++) d[i]=1ll*d[i]*k1%mod;
        exp(d,c,n-u);
        for(int i=u*k1,x=qpow(a[u],k2);i<n;i++) b[i]=1ll*c[i-u*k1]*x%mod;
    }
    void div(int *a,int *b,int *q,int *r,int n,int m)
    {///len(q)=n-m+1,len(r)<=m-1
        static int c[maxn],d[maxn],e[maxn];
        int len=n-m+1;
        for(int i=0;i<len;i++) c[i]=a[n-1-i];
        for(int i=0;i<len;i++) d[i]=i<m?b[m-1-i]:0;
        inv(d,e,len),mul(c,e,len,len),reverse(c,c+len),memcpy(q,c,4*len),mul(c,b,len,m);
        for(int i=0;i<m;i++) r[i]=dec(a[i],c[i]);
    }
}

温馨提示:

  • 调用 Poly::init() 函数进行初始化。
  • 长度上限 maxn 一般取 extend(n<<1) ,如 \(n=10^5\) 时,maxn=1<<18
  • 系数不能有负数,否则 add,dec 失效后 ntt 函数会爆 int
  • 传参数组长度类似 vectorsize 函数, \(\deg=n-1\)
  • 多项式快速幂更推荐使用注释中的方法,双 \(\log\) 用时仅为单 \(\log\) 的三倍,但代码简洁许多。

二、vector 版本

namespace Poly
{
    int r[maxn],w[maxn],inum[maxn];
    int qpow(int a,int k)
    {
        int res=1;
        for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
        return res;
    }
    int add(int x,int y)
    {
        if((x+=y)>=mod) x-=mod;
        return x;
    }
    int dec(int x,int y)
    {
        if((x-=y)<0) x+=mod;
        return x;
    }
    int extend(int n)
    {
        return 1<<(__lg(n-1)+1);
    }
    void get_r(int n)
    {
        for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|(i&1?n>>1:0);
    }
    void init(int n=maxn)
    {
        for(int k=2,m=1;k<=n;k<<=1,m<<=1)
        {
            w[m]=1;
            for(int i=m+1,x=qpow(3,(mod-1)/k);i<k;i++) w[i]=1ll*w[i-1]*x%mod;
        }
        for(int i=1;i<n;i++) inum[i]=qpow(i,mod-2);
    }
    void print(poly a,int n)
    {
        a.resize(n);
        for(int i=0;i<n;i++) printf("%d%c",a[i]," \n"[i==n-1]);
    }
    void ntt(poly &a,int n,int op)
    {
        for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]);
        for(int k=2,m=1;k<=n;k<<=1,m<<=1)
            for(int i=0;i<n;i+=k)
                for(int j=i,*x=w+m;j<i+m;j++,x++)
                {
                    int v=1ll*a[j+m]**x%mod;
                    a[j+m]=dec(a[j],v),a[j]=add(a[j],v);
                }
        if(op==-1)
        {
            reverse(a.begin()+1,a.begin()+n);
            int v=qpow(n,mod-2);
            for(int i=0;i<n;i++) a[i]=1ll*a[i]*v%mod;
        }
    }
    poly operator*(poly a,int b)
    {
        for(int i=0;i<a.size();i++) a[i]=1ll*a[i]*b%mod;
        return a;
    }
    poly operator+(poly a,poly b)
    {
        int n=max(a.size(),b.size());a.resize(n),b.resize(n);
        for(int i=0;i<n;i++) a[i]=add(a[i],b[i]);
        return a;
    }
    poly operator-(poly a,poly b)
    {
        int n=max(a.size(),b.size());a.resize(n),b.resize(n);
        for(int i=0;i<n;i++) a[i]=dec(a[i],b[i]);
        return a;
    }
    poly operator*(poly a,poly b)
    {
        int n=a.size(),m=b.size(),len=extend(n+m-1);
        a.resize(len),b.resize(len),get_r(len),ntt(a,len,1),ntt(b,len,1);
        for(int i=0;i<len;i++) a[i]=1ll*a[i]*b[i]%mod;
        return ntt(a,len,-1),a.resize(n+m-1),a;
    }
    void operator*=(poly &a,int b)
    {
        a=a*b;
    }
    void operator+=(poly &a,poly b)
    {
        a=a+b;
    }
    void operator-=(poly &a,poly b)
    {
        a=a-b;
    }
    void operator*=(poly &a,poly b)
    {
        a=a*b;
    }
    poly inv(poly a,int n)
    {
        n=extend(n),a.resize(n);
        poly b(n);b[0]=qpow(a[0],mod-2);
        for(int k=2;k<=n;k<<=1)
        {
            poly c(a.begin(),a.begin()+k),d(b.begin(),b.begin()+k);
            get_r(k),ntt(d,k,1);
            for(int i=0;i<k;i++) d[i]=1ll*d[i]*d[i]%mod;
            ntt(d,k,-1),c*=d;
            for(int i=0;i<k;i++) b[i]=(2ll*b[i]-c[i]+mod)%mod;
        }
        return b;
    }
    poly diff(poly a,int n)
    {
        poly b(n);
        for(int i=1;i<n;i++) b[i-1]=1ll*i*a[i]%mod;
        return b[n-1]=0,b;
    }
    poly integ(poly a,int n)
    {
        poly b(n);
        for(int i=1;i<n;i++) b[i]=1ll*inum[i]*a[i-1]%mod;
        return b[0]=0,b;
    }
    poly ln(poly a,int n)
    {
        assert(a[0]==1);
        n=extend(n),a.resize(n);
        return integ(inv(a,n)*diff(a,n),n);
    }
    poly exp(poly a,int n)
    {
        assert(a[0]==0);
        n=extend(n),a.resize(n);
        poly b={1,0};
        for(int k=2;k<=n;k<<=1)
        {
            poly c=ln(b,k);
            for(int i=0;i<k;i++) c[i]=dec(a[i],c[i]);
            c[0]++,b=b*c;
        }
        return b;
    }
    poly sqrt(poly a,int n)
    {
        static const int inv2=(mod+1)>>1;
        assert(a[0]==1);
        n=extend(n),a.resize(n);
        poly b(n);b[0]=1;
        for(int k=2;k<=n;k<<=1)
        {
            poly c(a.begin(),a.begin()+k);c*=inv(b,k);
            for(int i=0;i<k;i++) b[i]=1ll*(b[i]+c[i])*inv2%mod;
        }
        return b;
    }
    poly operator<<(poly a,int k)
    {
        poly b(a.size()+k);
        for(int i=0;i<a.size();i++) b[i+k]=a[i];
        return b;
    }
    poly operator>>(poly a,int k)
    {
        if(a.size()<=k) return {0};
        poly b(a.size()-k);
        for(int i=k;i<a.size();i++) b[i-k]=a[i];
        return b;
    }
    void operator<<=(poly &a,int k)
    {
        a=a<<k;
    }
    void operator>>=(poly &a,int k)
    {
        a=a>>k;
    }
    poly pow(poly a,int n,string k)
    {
        int u=0,k1=0,k2=0;
        for(int i=0;i<k.size();i++) k1=(10ll*k1+k[i]-'0')%mod,k2=(10ll*k2+k[i]-'0')%(mod-1);
        for(u=0;u<n&&!a[u];u++) ;
        if((u&&k.size()>5)||1ll*u*k1>=n) return poly(n);
        poly b(n),c(n-u);
        for(int i=u,x=qpow(a[u],mod-2);i<n;i++) c[i-u]=1ll*a[i]*x%mod;
        c=ln(c,n-u);
        for(int i=0;i<n-u;i++) c[i]=1ll*c[i]*k1%mod;
        c=exp(c,n-u);
        for(int i=u*k1,x=qpow(a[u],k2);i<n;i++) b[i]=1ll*c[i-u*k1]*x%mod;
        return b;
    }
    pair<poly,poly> div(poly a,poly b,int n,int m)
    {///len(q)=n-m+1,len(r)<=m-1
        a.resize(n),b.resize(m);
        if(n<m) return make_pair(poly{0},a);
        poly c=a,d=b;reverse(c.begin(),c.end()),c.resize(n-m+1),reverse(d.begin(),d.end());
        poly q=c*inv(d,n-m+1);q.resize(n-m+1),reverse(q.begin(),q.end());
        poly r=a-q*b;r.resize(m-1);
        return make_pair(q,r);
    }
    poly operator/(poly a,poly b)
    {
        return div(a,b,a.size(),b.size()).first;
    }
    poly operator%(poly a,poly b)
    {
        return div(a,b,a.size(),b.size()).second;
    }
    void operator/=(poly &a,poly b)
    {
        a=a/b;
    }
    void operator%=(poly &a,poly b)
    {
        a=a%b;
    }
}
using namespace Poly;

温馨提示:

  • 调用 Poly::init() 函数进行初始化。

  • 如果不涉及到分治 \(\texttt{NTT}\) ,个人不太推荐使用 vector 版本的板子。

    vector 版本代码长度比数组版本高 \(20\%\) ,运行效率比数组版本低 \(10\%\) ,很大程度上是因为构造新的 vector 时间开销较大。

    vector 版本由于涉及到重载运算符操作,所以不得不将 Poly 命名空间放入全局空间,但这很有可能导致变量重名。

三、快速傅里叶变换(FFT)

\(\texttt{FFT}\) 的核心思想:系数多项式转点值多项式(\(\texttt{DFT}\)),点值多项式可以直接相乘,点值多项式再转系数多项式(\(\texttt{IDFT}\))。

暴力求点值时间复杂度依然是 \(\mathcal O(n^2)\) ,但是如果\(n\) 补成 \(2\) 的方幂,并且利用单位根的性质,我们可以做到 \(\mathcal O(n\log n)\)

\(w_n^k=\cos\frac{2k\pi}n+i\sin\frac{2k\pi}n\) ,目标求 \(f(w_n^0),\cdots,f(w_n^{n-1})\)

令:

\[f(x)=a_0+a_1x+\cdots+a_{n-1}x^{n-1}\\ f_1(x)=a_0+a_2x+\cdots+a_{n-2}x^{\frac n2-1}\\ f_2(x)=a_1+a_3x+\cdots+a_{n-1}x^{\frac n2-1}\\ \]

容易发现 \(f(x)=f_1(x^2)+x\cdot f_2(x^2)\) ,对 \(0\le k\lt\frac n2\) ,分别代入 \(w_n^k\)\(w_n^{k+\frac n2}\)

\[f(w_n^k)=f_1(w_{\frac n2}^k)+w_n^k\cdot f_2(w_{\frac n2}^k)\\ f(w_n^{k+\frac n2})=f_1(w_{\frac n2}^k)-w_n^k\cdot f_2(w_{\frac n2}^k)\\ \]

时间复杂度 \(T(n)=2\cdot T(\frac n2)+\mathcal O(n)\) ,解得 \(T(n)=\mathcal O(n\log n)\)


递归版本常数太大,我们希望改成迭代版本。

观察一下使用 \(a_k\) 的次序,每次我们将偶数项(二进制下末位为 \(0\))放在前面,将奇数项(二进制下末位为 \(1\))放在后面。

\(2^d=n\) ,那么 \(d-1\) 轮操作等价于将 \(a_k\) 的二进制位翻转。

\(\forall 2\le i\le d\) ,维护自底向上迭代 \(i\) 次后的结果,每次通过 \(f_1(w_{2^{i-1}}^k)\)\(f_2(w_{2^{i-1}}^k)\) 计算 \(f(w_{2^i}^k)\)\(f(w_{2^i}^{k+2^{i-1}})\)

上述过程也被称为蝴蝶变换


\(w=w_n\)\(\texttt{DFT}\) 的本质是:

\[\begin{bmatrix} a_0\\ a_1\\ a_2\\ \vdots\\ a_{n-1}\\ \end{bmatrix} \times \begin{bmatrix} 1&1&1&\cdots&1\\ 1&w&w^2&\cdots&w^{n-1}\\ 1&w^2&w^4&\cdots&w^{2(n-1)}\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ 1&w^{n-1}&w^{2(n-1)}&\cdots&w^{(n-1)(n-1)}\\ \end{bmatrix} =\begin{bmatrix} f(w^0)\\ f(w^1)\\ f(w^2)\\ \vdots\\ f(w^{n-1}) \end{bmatrix} \]

读者可以验证转移矩阵的逆矩阵为:

\[\frac 1n\times \begin{bmatrix} 1&1&1&\cdots&1\\ 1&w^{-1}&w^{-2}&\cdots&w^{-(n-1)}\\ 1&w^{-2}&w^{-4}&\cdots&w^{-2(n-1)}\\ \vdots&\vdots&\vdots&\ddots&\vdots\\ 1&w^{-(n-1)}&w^{-2(n-1)}&\cdots&w^{-(n-1)(n-1)}\\ \end{bmatrix} \]

于是有了 \(\texttt{IDFT}\) 的第一种方法:用 \(w^{-1}\) 代替 \(w\) 重新做一遍 \(\texttt{FFT}\) ,最后除以 \(n\)


但是上面这种方法不利于卡常,究其原因是我们要同时预处理 \(w\)\(w^{-1}\) 的方幂。

于是有了第二种看待 \(\texttt{DFT}\) 的视角:

\[\texttt{DFT}[f(x)]=\sum_{i=0}^{n-1}f(w^i)x^i\\ \]

根据上面的分析,可以得到:

\[\texttt{IDFT}[f(x)]=\frac 1n\sum_{i=0}^{n-1}f(w^{-i})x^i\\ \]

如果将 \(g(x)=\texttt{IDFT}[f(x)]\) 视为 \(n\) 次多项式(原本是 \(n-1\) 次),则:

\[g_R(x)=\frac 1n\sum_{i=0}^{n-1}f(w^{n-i})x^{n-i}\\ \]

对比两边每一项的系数:

\[\begin{cases} [x^0]g(x)=[x^n]g_R(x)=\frac 1n[x^0]\texttt{DFT}[f(x)]\\ [x^i]g(x)=[x^{n-i}]g_R(x)=\frac 1n[x^{n-i}]\texttt{DFT}[f(x)]&1\le i\le n-1\\ \end{cases} \]

从而避开 \(w^{-1}\) ,完美。

四、快速数论变换(NTT)

\(\texttt{NTT}\) 使用条件: \(n\le 2^{v_2(p-1)}\)

单位根的性质原根全部满足,只需将 \(w_n\) 换成 \(g_n\) 即可。

重要卡常技巧:预处理所有 \(g_{2^i}^k\)

五、多项式求逆(inv)

给定 \(f(x)\) ,求 \(g(x)\) 满足 \(f(x)\cdot g(x)\equiv 1\pmod{x^n}\)

考虑倍增,假设已知 \(f(x)\cdot g_0(x)\equiv 1\pmod{x^\frac n2}\)

\[g(x)-g_0(x)\equiv 0\pmod{x^\frac n2}\\ g^2(x)-2\cdot g(x)\cdot g_0(x)+g_0^2(x)\equiv 0\pmod{x^n}\\ g(x)-2\cdot g_0(x)+f(x)\cdot g_0^2(x)\equiv 0\pmod{x^n}\\ g(x)\equiv 2\cdot g_0(x)-f(x)\cdot g_0^2(x)\pmod{x^n}\\ \]

六、多项式对数函数(ln)

给定 \(f(x)\) 满足**常数项为 \(1\) **,求 \(g(x)\) 满足 \(g(x)\equiv\ln f(x)\pmod{x^n}\)

两边对 \(x\) 求导:

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

求导求逆乘起来,再积分回去,即可得到 \(g(x)\)

七、多项式指数函数(exp)

前置知识:牛顿迭代。

牛顿迭代用于求函数零点

任取 \(x_0\) 作为初始值,取 \(x_1=x_0-\frac{f(x_0)}{f'(x_0)}\)\(x_0\) 处的切线与 \(x\) 轴的交点,依此类推。

牛顿迭代收敛速度非常快,对于多项式,每做一次精度就会加倍。


回到原题。

给定 \(f(x)\) 满足**常数项为 \(0\) **,求 \(g(x)\) 满足 \(g(x)\equiv e^{f(x)}\pmod{x^n}\)

题目等价于求 \(F(g(x))=\ln g(x)-f(x)\) 的零点,这里 \(f(x)\) 为常数, \(g(x)\) 为变量。

考虑倍增,假设 \(g_0(x)\)\(\bmod x^\frac n2\) 下的零点,则:

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

八、多项式开根(sqrt)

给定 \(f(x)\) 满足 **常数项为 \(1\) **,求 \(g(x)\) 满足 \(g^2(x)\equiv f(x)\pmod{x^n}\)

考虑倍增,假设 \(g_0^2(x)\equiv f(x)\pmod{x^\frac n2}\)

\[g(x)-g_0(x)\equiv 0\pmod{x^\frac n2}\\ g^2(x)-2\cdot g(x)\cdot g_0(x)+g_0^2(x)\equiv 0\pmod{x^n}\\ g(x)=\frac{f(x)+g_0^2(x)}{2\cdot g_0(x)}\equiv 0\pmod{x^n}\\ g(x)=\frac{\frac{f(x)}{g_0(x)}+g_0(x)}2\pmod{x^n}\\ \]

九、多项式快速幂(pow)

给定 \(f(x)\) ,求 \(g(x)\) 满足 \(g(x)\equiv f(x)^k\pmod{x^n}\)

方法一

指数 \(k\) 可对 \(p\) 取模,从而保证 \(k\lt p\)

类比普通快速幂,时间复杂度 \(\mathcal O(n\log n\log p)\) ,但常数小。

方法二

如果 \(f(x)\) 常数项为 \(1\) ,先取 \(\ln\) ,乘上 \(k\) 倍后再 \(\exp\) 即可。

如果 \(f(x)\) 常数项不为 \(1\) ,设 \(f(x)\) 的最低次项为 \(a_mx^m\) ,则:

\[g(x)=a_m^kx^{mk}(\frac{f(x)}{a_mx^m})^k \]

注意如果 \(p\ge n\),那么计算 \(a_m^k\)\(k\)\(\varphi(p)\) 取模,计算 \(f(x)^k\)\(k\)\(p\) 取模。

原因如下:

  • 对于一个非 \(p\) 的倍数 \(a_m\) ,由费马小定理 \(a_m^{p-1}=1\)

  • 对于一个常数项为 \(1\) 的多项式 \(f(x)\) ,我们证明 \(f(x)^p\equiv 1\pmod{x^n}\)

    \(a_i\) 取了 \(c_i\) 项,若不存在 \(c_i=p\) ,则广义组合数 \(\binom p{c_0,\cdots,c_n}\)\(p\) 的倍数。

    \(c_i=p\) ,当 \(i\ge 1\) 时, \((a_ix^i)^p\) 次数高于 \(n\) ,可以直接舍弃。

十、多项式除法 & 取模(div)

给定 \(f(x),g(x)\) 满足 \(\deg f=n\ge m=\deg g\) ,求 \(q(x),r(x)\) 满足 \(\deg q=n-m,\deg r\le m-1\) ,且 \(f(x)=q(x)\cdot g(x)+r(x)\)

对于次数为 \(k\) 的多项式 \(A(x)\) ,记 \(A_R(x)=x^kA(\frac 1x)\)

为方便起见,记 \(\deg r=m-1\)

\[f(x)=q(x)\cdot g(x)+r(x)\\ f(\frac 1x)=q(\frac 1x)\cdot g(\frac 1x)+r(\frac 1x)\\ x^nf(\frac 1x)=x^{n-m}q(\frac 1x)\cdot x^mg(\frac 1x)+x^{n-m+1}\cdot x^{m-1}r(\frac 1x)\\ f_R(x)=q_R(x)\cdot g_R(x)+x^{n-m+1}\cdot r_R(x)\\ f_R(x)\equiv q_R(x)\cdot g_R(x)\pmod{x^{n-m+1}}\\ q_R(x)\equiv f_R(x)\cdot g_R^{-1}(x)\pmod{x^{n-m+1}}\\ \]

至此我们可以求出 \(q(x)\) ,最后令 \(r(x)=f(x)-q(x)\cdot g(x)\) 即可。

注意求逆是在 \(\bmod x^{n-m+1}\) 而不是 \(x^m\) 意义下进行。

posted @ 2024-11-12 11:33  peiwenjun  阅读(2)  评论(0编辑  收藏  举报