• 多项式$fft$,$ntt$,$fwt$初步


    最近在学多项式和生成函数。

    上课听$lnc$大神讲还是$mengbier$。

    作为多项式的前置芝士,$fft,ntt$等是必学的。

    在此记录一些关于$fft,ntt,fwt$的知识及例题。。。


     FFT:

      应用在处理$sum _{i+j=k} f[i]*g[j]$的卷积上。

      看网上大佬的博客,基本入了门吧。

      自己的关于原理的一些见解:

        多项式有系数表示和点值表示,两种表示方法可以相互转化。

        FFT可以在$O(n*longn)$内解决多项式乘法。

        具体是,点值表示的方法应用在多项式上是$O(n)$的。

        那么如果能在快速时间内将多项式表示成点值,然后$O(n)$处理,再转化成多项式岂不美哉。

        于是可一取单位根作为代入多项式的$x$,用分治算法处理即可。

      板子:

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #define MAXN 362144
     5 using namespace std;
     6 const double PI=acos(-1);
     7 struct Cp{
     8     double x,y;
     9     Cp(double xx=0,double yy=0){x=xx;y=yy;return ; }
    10     friend Cp operator +(Cp a,Cp b){return Cp(a.x+b.x,a.y+b.y); }
    11     friend Cp operator -(Cp a,Cp b){return Cp(a.x-b.x,a.y-b.y); }
    12     friend Cp operator *(Cp a,Cp b){return Cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x); }
    13 }a[MAXN],b[MAXN];
    14 int n,m;
    15 int lim=1,l=-1;
    16 int ys[MAXN];
    17 inline void FFT(Cp *x,const int &lim,const int &type){
    18     for(int i=0;i<lim;++i)if(i<ys[i])swap(x[i],x[ys[i]]);
    19     for(int i=1;i<lim;i<<=1){
    20         Cp tmp(cos(PI/i),sin(PI/i)*type);
    21         for(int j=0,I=i<<1;j<lim;j+=I){
    22             Cp w(1,0);
    23             for(int k=0;k<i;++k,w=w*tmp){
    24                 Cp t1=x[j+k],t2=w*x[k+i+j];
    25                 x[j+k]=t1+t2,x[k+i+j]=t1-t2;
    26             }
    27         }
    28     }
    29     return ;
    30 }
    31 int main(){
    32     //freopen("da.in","r",stdin);
    33     
    34     scanf("%d%d",&n,&m);
    35     for(int i=0;i<=n;++i)scanf("%lf",&a[i].x);
    36     for(int i=0;i<=m;++i)scanf("%lf",&b[i].x);
    37     n+=m;
    38     for(;lim<=n;lim<<=1,++l);
    39     for(int i=0;i<lim;++i)ys[i]=(ys[i>>1]>>1)|((i&1)<<l);
    40     FFT(a,lim,1);FFT(b,lim,1);
    41     for(int i=0;i<lim;++i)a[i]=a[i]*b[i];
    42     FFT(a,lim,-1);
    43     for(int i=0;i<=n;++i)printf("%d ",(int)(a[i].x/lim+0.5));
    44     puts("");
    45     
    46     return 0;
    47 }
    View Code

    NTT:

      原理和$FFT$类似。

     1 #include<cstdio>
     2 #include<iostream>
     3 #define MAXN 2197152
     4 #define LL long long
     5 #define swap(x,y) (x^=y,y^=x,x^=y)
     6 using namespace std;
     7 const LL mod=998244353;
     8 const LL G=3,inv_G=332748118;
     9 int n,m;
    10 int lim=1,l=-1;
    11 int ys[MAXN];
    12 LL a[MAXN],b[MAXN];
    13 LL qpow(LL a,LL b){
    14     LL res=1LL;a%=mod;
    15     for(;b;b>>=1,a=a*a%mod)if(b&1)res=res*a%mod;
    16     return res%mod;
    17 }
    18 inline void NTT(LL *x,const int lim,const int type){
    19     for(int i=0;i<lim;++i)if(i<ys[i])swap(x[i],x[ys[i]]);
    20     for(int i=1;i<lim;i<<=1){
    21         const int delta=i<<1;
    22         const LL tmp=qpow(type==1?G:inv_G,(mod-1)/delta);
    23         for(int j=0;j<lim;j+=delta){
    24             LL w=1LL;
    25             for(int k=0;k<i;++k,w=w*tmp%mod){
    26                 LL t1=x[j+k],t2=w*x[i+j+k]%mod;
    27                 x[j+k]=(t1+t2)%mod,x[i+j+k]=(t1-t2+mod)%mod;
    28             }
    29         }
    30     }
    31     return ;
    32 }
    33 int main(){
    34     //freopen("da.in","r",stdin);
    35     
    36     scanf("%d%d",&n,&m);
    37     for(int i=0;i<=n;++i)scanf("%lld",&a[i]);
    38     for(int i=0;i<=m;++i)scanf("%lld",&b[i]);
    39     n+=m;for(;lim<n+2;lim<<=1,++l);
    40     for(int i=0;i<lim;++i)ys[i]=(ys[i>>1]>>1)|((i&1)<<l);
    41     NTT(a,lim,1);NTT(b,lim,1);
    42     for(int i=0;i<lim;++i)a[i]=a[i]*b[i]%mod;
    43     NTT(a,lim,-1);
    44     LL _inv=qpow(lim,mod-2);
    45     for(int i=0;i<=n;++i)printf("%lld ",a[i]*_inv%mod);
    46     puts("");
    47     
    48     return 0;
    49 }
    View Code

    序列统计:

      一道很棒的题,曾经出在达哥的模拟题中。

      不过因为当时$M$的范围很小,$O(log(N)*M^2)$水过了。

      然而此题必须得学原根了。

      原根的两个知识点:

        1>求法:拆$phi(M)$的质因子,从$2$暴枚,满足时当且仅当所有的$x^{frac{phi(y)}{d}}!=1$。

        2>对于一个在$mod$意义下的原根$x$,$x^1,x^2,x^3.....x^{phi(mod)}$可以取遍$[1,phi(mod)]$的数,

        其中,有费马小定理$a^{phi(p)}equiv 1(mod p)$,所以$x^{phi(mod)}$等同于$x^0$。

      对这道题有什么用?

      首先有倍增优化的$dp[i]$,表示某长度下,此时结果为$i$的方案数。

      转移为$dp[i*j mod P]=sum dp[i]*b[j]$,好像每次转移只能$O(n^2)$。

      此时考虑应用原根,乘法转化成了加法。

      因为每个数都能用原根的次幂表示,所以可以直接用指数来表示一个数。

      即为$dp[(i+j) mod phi(P)]=sum dp[i]*b[j]$

      变成了我们会优化的形式,每次$NTT$加速即可。

      注意$0in S$,不过$x>=1$,所以直接把$0$舍去即可。

      $O(log(N)*M*log(M))$


     万径人踪灭:

      题目背景保先生以及诗句的含义,啧啧。。。

      没有看出来答案为$不连续回文序列个数-连续回文序列个数$。

      求后者可以$O(n)$马拉车,也可以二分加$hash$。

     1 LL Manacher(){
     2     LL res=0;
     3     int j=0;
     4     for(int i=1;i<=len;++i)nw[++j]='#',nw[++j]=rn[i];
     5     nw[++j]='#';
     6     int mx=0,id=0;
     7     //cout<<j<<endl;
     8     for(int i=1;i<=j;++i){
     9         p[i]=(i<mx)?min(p[2*id-i],mx-i):1;
    10         while(i-p[i]>=1&&i+p[i]<=j&&nw[i-p[i]]==nw[i+p[i]])++p[i];
    11         if(mx<i+p[i])mx=i+p[i],id=i;
    12         int t=p[i]-1;
    13         //if(rn[i]=='#')--t;
    14         (res+=(t+1)/2)%=mod;
    15         //cout<<i<<" "<<p[i]<<" "<<(t+1)/2<<endl;
    16     }
    17     //cout<<"qwq"<<endl;
    18     //scout<<res<<endl;
    19     return res;
    20 }
    manacher

      前者是可将个数拆成$2^{cnt(a)+cnt(b)+(len and 1)}$。

      求$cnt$,可以让$a$为$1$,$b$为$0$,$FFT$后$f[i]$即为$mid*2$处左右对称$a$的个数。


    染色:

      首先可以简单推一个$f[i]=frac{C(M,i)*C(N,i*S)*A(i*S,i*S)}{A(S,S)^i}*(M-i)^{N-S*i}$。

      为钦定有$i$个颜色恰好出现了$S$次,我们想要的到$g[i]$为恰好有$i$个颜色出现了$S$次。

      考虑有什么联系,发现因为不能保证剩下的颜色是否还会出现$S$次,所以会有重复的。

      具体,$j>=i$,$g[j]$会在$f[i]$中算重了$C(j,i)$次,于是有:

      $f[i]=sum_{j=i}^{m}C(j,i)*g[j]$

      为二项式反演形式,变成:

      $g[i]=sum_{j=i}^{m}-1^{j-i}C(j,i)*f[j]$

      然后把阶乘拆掉就变成卷积的形式辣。


      

      

      

  • 相关阅读:
    第三次冲刺--软件工程
    【操作系统】实验四 主存空间的分配和回收
    《构造之法》8、9、10
    实验三 进程调度模拟程序--操作系统
    构建之法读后感
    操作系统作业调度-操作系统
    结对评论—软件工程
    复利计算6.0—软件工程(web版本)
    学习进度条
    第三次冲刺总结
  • 原文地址:https://www.cnblogs.com/2018hzoicyf/p/12028151.html
Copyright © 2020-2023  润新知