• 「学习笔记」多项式


    「学习笔记」多项式

    把一直学不懂的各种大常数 (O(nlog n)) 的神奇多项式算法总结一下……

    证明什么的比较简略……

    还有我今天才知道预处理一下单位根会快很多 (qwq)

    1、(FFT)

    最没用的一个

    首先我们能写出一个 (O(n^2)) 的暴力 这个你都不会就可以退役了(某位dalao题解中的)

    要确定一个多项式,我们发现只要代 (f(1),f(2),f(3),...,f(n+m)) 然后暴力插值就可以了。

    还是不行。但是聪明的数学家们发现单位根有分治的特性,然后 (FFT) 就发明出来了。

    (FFT) 分两步,一步 (DFT),一步 (IDFT)

    (Complex:)

    struct Complex{
    	double x,y;
    	Complex(double u=0,double v=0){x=u,y=v;}
    };
    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);
    }
    

    (FFT:)

    inline void FFT(Complex *f,int op){
    	for(int i=0;i<n;i++)
    		if(i<r[i]) swap(f[i],f[r[i]]);
    	Complex tmp,buf,x,y;
    	for(int len=1;len<n;len<<=1){
    		tmp=Complex(cos(Pi/len),op*sin(Pi/len));
    		for(int R=len<<1,j=0;j<n;j+=R){
    			buf=Complex(1,0);
    			for(int k=0;k<len;k++){
    				x=f[j+k],y=buf*f[j+k+len];
    				f[j+k]=x+y;f[j+k+len]=x-y;
    				buf=buf*tmp;
    			}
    		}
    	}
    	if(op==1) return ;
    	for(int i=0;i<n;i++) f[i].x=fabs(f[i].x/n);
    }
    

    2、(NTT)

    这个更有用。。。

    把复数换成原根什么的就可以了。

    预处理:

    inline int add(int x,int y){
        x+=y;x>=mod?x-=mod:0;
        return x;
    }
    inline int sub(int x,int y){
        x-=y;x<0?x+=mod:0;
        return x;
    }
    inline int mul(int x,int y){
        return 1ll*x*y%mod;
    }
    inline void calcrev(int lim){
        for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)?(lim>>1):0);
    }
    inline int fpow(int a,int b){
        int ret=1;
        for(;b;b>>=1,a=mul(a,a))
            if(b&1) ret=mul(ret,a);
        return ret;
    }
    for(int len=1,l=0;len<=mod;len<<=1,l++){
            G[l][0]=fpow(3,(mod-1)/len);
            G[l][1]=fpow(G[l][0],mod-2);
        }
    

    (NTT:)

    inline void NTT(int *f,int n,int op){
        for(int i=0;i<n;i++)
            if(i<r[i]) swap(f[i],f[r[i]]);
        int buf,tmp,x,y;
        for(int len=1,l=1;len<n;len<<=1,l++){
            tmp=G[l][0];
            if(op==-1) tmp=G[l][1];
            for(int i=0;i<n;i+=len<<1){
                buf=1;
                for(int j=0;j<len;j++){
                    x=f[i+j];y=mul(buf,f[i+j+len]);
                    f[i+j]=add(x,y);f[i+j+len]=sub(x,y);
                    buf=mul(buf,tmp);
                }
            }
        }
        if(op==1) return ;
        int inv=fpow(n,mod-2);
        for(int i=0;i<n;i++) f[i]=mul(f[i],inv);
    }
    

    还有比较懒写了几个辅助函数:

    inline void Mul(int *A,int *B,int *C,int n,int m){
        int lim;
        for(lim=1;lim<(n+m);lim<<=1);
        calcrev(lim);
        NTT(A,lim,1);NTT(B,lim,1);
        for(int i=0;i<lim;i++) C[i]=mul(A[i],B[i]);
        NTT(C,lim,-1);
    }
    inline void Clear(int *A,int *B,int *C,int *D,int n){
        int lim;
        for(lim=1;lim<(n<<1);lim<<=1);
        for(int i=0;i<lim;i++) A[i]=B[i]=C[i]=0;
        for(int i=n;i<lim;i++) D[i]=0;
    }
    inline void Clear(int *A,int *B,int *C,int n){
        int lim;
        for(lim=1;lim<n;lim<<=1);
        for(int i=0;i<lim;i++) A[i]=B[i]=C[i]=0;
    }
    

    3、多项式求逆

    倍增。

    (F(x)*G(x)equiv 1(mod x^{lceil frac n2 ceil}))

    (F(x)*H(x)equiv 1(mod x^n))

    (G(x)-H(x)equiv 0(mod x^{lceil frac n2 ceil}))

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

    (G^2(x)-2*G(x)*H(x)+H^2(x)equiv 0(mod x^n))

    (F(x)*G^2(x)-2*G(x)*F(x)*H(x)+F(x)*H^2(x)equiv 0(mod x^n))

    (F(x)*G^2(x)-2*G(x)+H(x)equiv 0(mod x^n))

    (H(x)=2*G(x)-F(x)*G^2(x)(mod x^n))

    (Inv:)

    inline void Inv(int *a,int *b,int n){
        b[0]=fpow(a[0],mod-2);
        static int A[maxn],B[maxn],C[maxn],len,lim;
        for(len=1;len<(n<<1);len<<=1){
            lim=len<<1;
            for(int i=0;i<len;i++) A[i]=a[i],B[i]=b[i];
            calcrev(lim);
            NTT(A,lim,1);NTT(B,lim,1);
            for(int i=0;i<lim;i++) C[i]=mul(sub(2,mul(A[i],B[i])),B[i]);
            NTT(C,lim,-1);
            for(int i=0;i<len;i++) b[i]=C[i];
        }
        Clear(A,B,C,b,n);
    }
    

    4、多项式除法+取模

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

    (F(frac 1x)=Q(frac 1x)*G(frac 1x)+R(frac 1x))

    (x^nF(frac 1x)=x^{n-m}Q(frac 1x)*x^mG(frac 1x)+x^{n-m+1}*x^{m-1}*R(frac 1x))

    (F_R(x)=Q_R(x)*G_R(x)+x^{n-m+1}*R_R(x))

    (F_R(x)=Q_R(x)*G_R(x)(mod x^{n-m+1}))

    然后到这里 (Q_R(x)) 就可以求出来了,求出来由 (R(x)=F(x)-Q(x)*G(x)) 求出 (R(x))

    (Div:)

    inline void Div(int *a,int *b,int *c,int n,int m){
        static int A[maxn],B[maxn],C[maxn];
        reverse(a,a+n);reverse(b,b+m);
        for(int i=0;i<n-m+1;i++) A[i]=a[i];
        Inv(b,B,n-m+1);
        Mul(A,B,C,n-m+1,n-m+1);
        for(int i=0;i<n-m+1;i++) c[i]=C[i];
        reverse(c,c+n-m+1);
        reverse(a,a+n);reverse(b,b+m);
        Clear(A,B,C,(n-m+1)<<1);
    }
    

    (Mod:)

    inline void Mod(int *a,int *b,int *c,int n,int m){
        static int A[maxn],B[maxn],C[maxn];
        for(int i=0;i<m;i++) A[i]=b[i];
        Div(a,b,B,n,m);
        Mul(A,B,C,n-m+1,m);
        for(int i=0;i<m-1;i++) c[i]=sub(a[i],C[i]);
        Clear(A,B,C,n+1);
    }
    

    5、多项式开根

    (H^2(x)equiv F(x)(mod x^{lceilfrac n2 ceil}))

    (G(x)equiv H(x)(mod x^{lceilfrac n2 ceil}))

    (G(x)-H(x)equiv 0(mod x^{lceilfrac n2 ceil}))

    ((G(x)-H(x))^2equiv 0(mod x^{lceilfrac n2 ceil}))

    (G^2(x)-2H(x)*G(x)+H^2(x)equiv 0(mod x^n))

    (F(x)-2H(x)*G(x)+H^2(x)equiv 0(mod x^n))

    (G(x)=Large frac {F(x)+H^2(x)}{2H(x)})

    (Sqrt:)

    inline void Sqrt(int *a,int *b,int n){
        b[0]=1;
        static int A[maxn],B[maxn],C[maxn],len;
        for(len=1;len<(n<<1);len<<=1){
            for(int i=0;i<len;i++) A[i]=a[i];
            for(int i=0;i<len;i++) B[i]=0;//用static的话这里一定要清零
            Inv(b,B,len);
            Mul(A,B,C,len,len);
            for(int i=0;i<len;i++) b[i]=mul(add(b[i],C[i]),inv2);
        }
        Clear(A,B,C,b,n);
    }
    

    6、分治 (FFT)

    用求逆的话不是很傻吗。。。

    我们考虑 ([l,mid])([mid+1,r]) 的贡献,构造多项式卷积。

    (CDQFFT:)

    void CDQFFT(int *f,int *g,int l,int r){
        if(l==r) return ;
        int mid=(l+r)>>1,lim;
        CDQFFT(f,g,l,mid);
        for(lim=1;lim<=((r-l+1)<<1);lim<<=1);
        for(int i=l;i<=mid;i++) A[i-l]=f[i];
        for(int i=0;i<=r-l;i++) B[i]=g[i];
        calcrev(lim);
        NTT(A,lim,1);NTT(B,lim,1);
        for(int i=0;i<lim;i++) A[i]=1ll*A[i]*B[i]%mod;
        NTT(A,lim,-1);
        for(int i=mid+1;i<=r;i++) f[i]=(f[i]+A[i-l])%mod;
        for(int i=0;i<lim;i++) A[i]=B[i]=0;
        CDQFFT(f,g,mid+1,r);
    }
    

    7、多项式多点求值

    构造多项式 (prod_{i=l}^{r}(x-a_i)),类似分治 (FFT) 的思路,但是要用到多项式取模。

    我们用类似线段树的方式存下所有多项式。

    (Build+Eval:)

    void Build(int *a,int l,int r,int x){
        if(l==r){
            P[x].push_back(mod-a[l]);
            P[x].push_back(1);
            return ;
        }
        int mid=(l+r)>>1;
        Build(a,l,mid,x<<1);
        Build(a,mid+1,r,x<<1|1);
        int n=P[x<<1].size(),m=P[x<<1|1].size();
        static int A[maxn],B[maxn],C[maxn];
        for(int i=0;i<n;i++) A[i]=P[x<<1][i];
        for(int i=0;i<m;i++) B[i]=P[x<<1|1][i];
        Mul(A,B,C,n,m);
        for(int i=0;i<n+m-1;i++) P[x].push_back(C[i]);
        Clear(A,B,C,n+m);
    }
    void Eval(int *a,int *b,int dep,int n,int l,int r,int x){
        int mid=(l+r)>>1,m=P[x].size();
        static int A[maxn];int *B=Q[dep];
        for(int i=0;i<m;i++) A[i]=P[x][i];
        Mod(a,A,B,n,m);
        if(l==r){b[l]=B[0];return;}
        Eval(B,b,dep+1,m-1,l,mid,x<<1);
        Eval(B,b,dep+1,m-1,mid+1,r,x<<1|1);
        Clear(A,B,B,m-1);
    }
    

    8、多项式快速插值

    咕咕咕。

    (calc+solve:)

    void calc(int *a,int *b,int dep,int l,int r,int x){
        if(l==r){b[0]=a[l];return;}
        int mid=(l+r)>>1,n=P[x<<1].size(),m=P[x<<1|1].size();
        static int A[maxn],C[maxn];int *B=Q[dep];
        calc(a,B,dep+1,l,mid,x<<1);
        for(int i=0;i<m;i++) A[i]=P[x<<1|1][i];
        Mul(A,B,C,m,n);
        for(int i=0,j=P[x].size();i<j;i++) b[i]=add(b[i],C[i]);
        Clear(A,B,C,n+m);
        calc(a,B,dep+1,mid+1,r,x<<1|1);
        for(int i=0;i<n;i++) A[i]=P[x<<1][i];
        Mul(A,B,C,n,m);
        for(int i=0,j=P[x].size();i<j;i++) b[i]=add(b[i],C[i]);
        Clear(A,B,C,n+m);
    }
    inline void solve(int *a,int *b,int *c,int n){
        Build(a,0,n-1,1);
        int m=P[1].size();
        static int A[maxn],B[maxn];
        for(int i=0;i<m;i++) A[i]=P[1][i];
        Direv(A,A,m);Eval(A,B,0,m-1,0,n-1,1);
        for(int i=0;i<n;i++) B[i]=mul(b[i],fpow(B[i],mod-2));
        calc(B,c,0,0,n-1,1);
        for(int i=0;i<n;i++) A[i]=B[i]=0;
    }
    

    前五个是 (O(nlog n)) 的,后面三个是 (O(nlog^2 n)) 的。

    (Upd:) 居然忘了多项式 (Ln)(Exp)。。。

    9、(MTT)

    拆系数 (FFT),实际使用比三模数要快。不过我的 (MTT) 必须要开 (long double) 是什么鬼啊。。。

    (MTT:)

    inline void Mul(int *a,int *b,int *c,int n,int m){
        int lim;
        for(lim=1;lim<(n+m);lim<<=1);
        for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)?(lim>>1):0);
        for(int i=0;i<lim;i++){
            a[i]%=mod;b[i]%=mod;
            A[i]=Complex(a[i]/base,0);
            B[i]=Complex(a[i]%base,0);
            C[i]=Complex(b[i]/base,0);
            D[i]=Complex(b[i]%base,0);
        }
        FFT(A,lim,1);FFT(B,lim,1);FFT(C,lim,1);FFT(D,lim,1);
        Complex tmp;
        for(int i=0;i<lim;i++){
            tmp=A[i]*C[i];C[i]=B[i]*C[i];
            B[i]=B[i]*D[i];D[i]=D[i]*A[i];
            A[i]=tmp;C[i]=C[i]+D[i];
        }
        FFT(A,lim,-1);FFT(B,lim,-1);FFT(C,lim,-1);
        for(int i=0;i<lim;i++){
        	c[i]=0;
            c[i]=(c[i]+(ll)A[i].x%mod*base%mod*base%mod)%mod;
            c[i]=(c[i]+(ll)C[i].x%mod*base%mod)%mod;
            c[i]=(c[i]+(ll)B[i].x%mod)%mod;
            c[i]=(c[i]+mod)%mod;
        }
    }
    
  • 相关阅读:
    注册表清理bat
    apache2.2.14 负载均衡过程中遇到问题记录;
    thikpad 中eclipse/idea 无法获取鼠标
    PLSQL DEVELOPER 提示(错误)信息是乱码;亲测有效
    windows右键 打开方式 浏览 无法添加默认打开方式 解决
    澳洲值得代购物品汇总
    5 open source dashboard tools for visualizing data
    nginx概述
    Linux bash 参数处理办法
    Linux shell 启动配置文件设置
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/10353700.html
Copyright © 2020-2023  润新知