• OI多项式 简单学习笔记


    咕咕咕 先开个坑(其实是存模板来了)

    一些特别简单的前置东西qwq

    复数的计算

    复数相加:向量相加,复数相乘。复数相乘:模长相乘,旋转量相加(就是复平面坐标轴逆时针旋转的角度)
    (当然也可以直接使用complex类,.real()即取实数部分)

    单位根

    主n次单位根:(w_n=e^{2pi i/n})
    n个n次单位复数根可以用(w_n^0,w_n^1,...,w_n^{n-1})

    性质

    • n次单位根的次幂还是n次单位根
    • (w_n^n=1),n为偶数时(w_n^{n/2}=-1)
    • (sum_{i=0}^{n-1}w_n^i=0)(证明方式:等比数列求和)
    • 全部单位根将复平面上单位圆n等分
    • (w_{2n}^{2k}=w_n^k)
    • (w_n^{k+frac{n}{2}}=-w_n^k)

    原根

    阶的定义:

    设a,p是整数,a和p互素,那么:
    使(a^nequiv1pmod p)成立的最小正整数n叫做a模p的阶。

    原根的定义:

    原根,是一个数学符号。设m是正整数,a是整数,若a模m的阶等于(phi)(m),则称a为模m的一个原根。

    原根的性质:

    • 对于正整数x,只有(x=2,4,p^a,2 imes p^a),p为奇素数,a>=1的时候才存在原根。
    • (a^1,a^2,a^3,...,a^{phi(m)})在模p的时候都不相同(可以推得它构成了一个长度为(phi(m))的循环节)
    • 对于m的原根g,满足gi(mod p)(0<=i<=p-2)与1到p-1一一对应.
    • 若m有原根,那么它的原根个数为(phi(phi(m)))。(具体证明蒟蒻不是特别会qwq,上课没听懂)

    对于m的情况最为常用。在这种情况下,0~m-1中每一个数都可以对应一个(a^x),这就相当于模意义下取对数(离散对数)。我们可以化乘为加。或者快速地计算一个数的若干次幂。

    判断一个数a是不是m的原根

    即判断是否不存在(1<=x<phi(m))。由于(a^{phi(m)}equiv pmod m),我们只需要判断x为(phi(m))的因数的情况就可以了,时间复杂度(O(sqrt(m)log m))
    实际上,对于(phi(m))的每种质因数p_i判断(frac{phi(m)}{p_i})也是等价的。
    求出原根后,用暴力或者BSGS就可以求出来一个数的对数。
    ps.一个数的原根是很多的,我们可以从小到大判断,或者随机化选取数字判断。

    完全是背模板的FFT

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #define MAXN 2700010
    using namespace std;
    int n,m,cnt;
    int r[MAXN];
    const double PI=acos(-1.0);
    struct complex
    {
        double x,y;
        complex(double xx=0,double yy=0){x=xx,y=yy;}
    }a[MAXN],b[MAXN];
    complex operator +(complex a,complex b){return complex(a.x+b.x,a.y+b.y);}
    complex operator -(complex a,complex b){return complex(a.x-b.x,a.y-b.y);}
    complex operator *(complex a,complex b){return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
    inline void fft(complex *p,int opt)
    {
        for(int i=0;i<n;i++) if(i<r[i]) swap(p[i],p[r[i]]);
        for(int i=1;i<n;i<<=1)
        {
            complex W=complex(cos(PI/i),opt*sin(PI/i));
            for(int r=i<<1,j=0;j<n;j+=r)
            {
                complex w(1,0);
                for(int k=0;k<i;k++,w=w*W)
                {
                    complex X=p[j+k],Y=w*p[j+i+k];
                    p[j+k]=X+Y;
                    p[j+i+k]=X-Y;
                }
            }
        }
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++) scanf("%lf",&a[i]);
        for(int i=0;i<=m;i++) scanf("%lf",&b[i]);
        m+=n;
        for(n=1;n<=m;n<<=1) ++cnt;
        for(int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(cnt-1));
        fft(a,1);
        fft(b,1);
        for(int i=0;i<=n;i++) a[i]=a[i]*b[i];
        fft(a,-1);
        for(int i=0;i<=m;i++) printf("%d ",(int)(a[i].x/n+0.5));
        return 0;
    }
    

    继续背模板的NTT

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define mod 998244353
    #define MAXN 5000010
    using namespace std;
    int n,m,N,M,gr=3,l;
    int r[MAXN],a[MAXN],b[MAXN];
    inline int fpow(int x,int y)
    {
        int cur_ans=1;
        while(y)
        {
            if(y&1) cur_ans=1ll*cur_ans*x%mod;
            x=1ll*x*x%mod;
            y>>=1;
        }
        return cur_ans;
    }
    inline void ntt(int *P,int opt)
    {
        for(int i=0;i<N;i++) 
            if(i<r[i])
                swap(P[i],P[r[i]]);
        for(int i=1;i<N;i<<=1)
        {
            int W=fpow(gr,(mod-1)/(i*2));
            for(int p=i<<1,j=0;j<N;j+=p)
            {
                int w=1;
                for(int k=0;k<i;k++,w=1ll*w*W%mod)
                {
                    int X=P[j+k],Y=1ll*w*P[j+k+i]%mod;
                    P[j+k]=(X+Y)%mod,P[j+k+i]=(X+mod-Y)%mod;
                }
            }
        }
        if(opt==-1) reverse(&P[1],&P[N]);
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++) scanf("%d",&a[i]);
        for(int i=0;i<=m;i++) scanf("%d",&b[i]);
        N=n,M=m;
        M+=N;
        for(N=1;N<=M;N<<=1) l++;
        for(int i=0;i<N;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
        ntt(a,1),ntt(b,1);
        for(int i=0;i<N;i++) a[i]=1ll*a[i]*b[i]%mod;
        ntt(a,-1);
        for(int i=0,inv=fpow(N,mod-2);i<=M;i++) a[i]=1ll*a[i]*inv%mod;
        for(int i=0;i<=M;i++) printf("%d ",a[i]); puts("");
        return 0;
    }
    

    所以说分治FFT是个什么东西

    多项式求逆

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define gr 3
    #define MAXN 1000010
    #define mod 998244353
    using namespace std;
    int n,m,N,M,l;
    int r[MAXN],A[MAXN],B[MAXN],X[MAXN],Y[MAXN];
    inline int fpow(int x,int y)
    {
        int cur_ans=1;
        while(y)
        {
            if(y&1) cur_ans=1ll*cur_ans*x%mod;
            x=1ll*x*x%mod;
            y>>=1;
        }
        return cur_ans;
    }
    inline void ntt(int *P,int opt,int len)
    {
        l=0;
        for(N=1;N<len;N<<=1) l++;
        for(int i=0;i<N;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
        for(int i=0;i<N;i++)
            if(i<r[i])  
                swap(P[i],P[r[i]]);
        for(int i=1;i<N;i<<=1)
        {
            int W=fpow(gr,(mod-1)/(i<<1));
            for(int j=0,p=i<<1;j<N;j+=p)
            {
                int w=1;
                for(int k=0;k<i;k++,w=1ll*w*W%mod)
                {
                    int X=P[j+k],Y=1ll*w*P[j+k+i]%mod;
                    P[j+k]=(X+Y)%mod,P[j+k+i]=(X+mod-Y)%mod;
                }
            }
        }
        if(opt==-1) reverse(&P[1],&P[N]);
    }
    inline void get_inv(int *a,int *b,int len)
    {
        if(len==1){b[0]=fpow(a[0],mod-2)%mod;return;}
        get_inv(a,b,len>>1);
        for(int i=0;i<len;i++) A[i]=a[i],B[i]=b[i];
        ntt(A,1,len<<1),ntt(B,1,len<<1);
        for(int i=0;i<(len<<1);i++) A[i]=1ll*A[i]*B[i]%mod*B[i]%mod;
        ntt(A,-1,len<<1);
        for(int i=0,inv=fpow(N,mod-2);i<N;i++) A[i]=1ll*A[i]*inv%mod;
        for(int i=0;i<len;i++) b[i]=2*b[i]%mod;
        for(int i=0;i<len;i++) b[i]=(b[i]+mod-A[i])%mod;
        for(int i=0;i<(len<<1);i++) A[i]=B[i]=0;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d",&X[i]);
        for(N=1;N<n;N<<=1);
        get_inv(X,Y,N);
        for(int i=0;i<n;i++) printf("%d ",Y[i]); puts("");
        return 0;
    }
    

    多项式开根

    多项式除法

    多项式对数

    多项式指数

    多项式快速幂

  • 相关阅读:
    [leetCode]Reverse Words in a String
    [leetCode]Word Break
    [leetCode]Unique Binary Search Trees
    [leetCode]Binary Tree Inorder Traversal 递归 && 栈解法
    [leetCode]Binary Tree Zigzag Level Order Traversal
    [leetCode]Binary Tree Level Order Traversal
    [leetCode]Binary Tree Maximum Path Sum
    freemarker生成静态页面中文乱码(固定中文和动态生成的中文都乱码)
    多线程-安全的终止线程
    多线程-理解中断
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10830955.html
Copyright © 2020-2023  润新知