本总结主要用于帮助个人理解,讲得不足之处,还请各位看官谅解

FFT

补充知识

\(n\)次单位复根(\(w_n\)):

使得\(z^n=1\)的一类复数,这些复数一共有\(n\)个,它们都分布在复平面的单位圆上,并且连线构成一个正\(n\)边形

点值表示:

多项式\(f(x)=\sum_{i=0}^{n-1}{a_ix^i}\)的点值表示为\(n\)个点\((x_i,y_i)\),其中\(y_i=f(x_i)\)

递归算法主要思路

由折半引理\(w_{2n}^{2k}=w_n^k\),将其代入可得:

\[f(w_{n}^{k})=\sum_{i=0}^{n-1}{a_iw_{n}^{ki}}\]

\[=\sum_{i=0}^{\frac{n}{2}-1}{a_{2i}w_n^{2ki}}+w_n^{k}\sum_{i=0}^{\frac{n}{2}-1}{a_{2i+1}w_n^{2ki}}\]

\[=\sum_{i=0}^{\frac{n}{2}-1}{a_{2i}w_{\frac{n}{2}}^{ki}}+w_n^{k}\sum_{i=0}^{\frac{n}{2}-1}{a_{2i+1}w_{\frac{n}{2}}^{ki}}\]

而且由于\(w_n^{k+\frac{n}{2}}=-w_n^k\),

所以

\[f(w_{n}^{k+\frac{n}{2}})=\sum_{i=0}^{\frac{n}{2}-1}{a_{2i}w_{\frac{n}{2}}^{ki}}+w_n^{k+\frac{n}{2}}\sum_{i=0}^{\frac{n}{2}-1}{a_{2i+1}w_{\frac{n}{2}}^{ki}}\]

\[=\sum_{i=0}^{\frac{n}{2}-1}{a_{2i}w_{\frac{n}{2}}^{ki}}-w_n^k\sum_{i=0}^{\frac{n}{2}-1}{a_{2i+1}w_{\frac{n}{2}}^{ki}}\]

逐步递归下去,最终只需要代入\(w_1^0\)的值\((1)\)并在回溯的过程中不断更新该表达式的项即可

最后每一个系数\(a_i\)都会被转换成点值表示中的\(y_i(f(w_n^{i}))\)

非递归算法主要思路

Rader:由于最后递归完毕的编号为该编号对应下标的二进制表示的倒序

故将系数预先排好序,最后再一层层向上更新即可

Rader实现的一行代码(r[i]表示i倒序后对应的编号):

for(RG int i=0;i<n;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));

主要思想其实就是把\(\times2\)的操作转换成了\(>>1\),再根据情况考虑是否要在最高位\(+1\)

极大地减小了常数啊

代码

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<complex>
#include<cstring>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define isr(i) (t[t[i].fa].s[1]==i)
#define ls t[i].s[0]
#define rs t[i].s[1]
#define pb push_back
#define RG register
#define il inline
using namespace std;
const double pi=acos(-1);
const int mod=666623333;
const int N=3000010;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
il ll read(){
    RG ll data=0,w=1;RG char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
    return data*w;
}

struct Complex{double r,i;};
Complex operator +(Complex a,Complex b)
{return (Complex){a.r+b.r,a.i+b.i};}
Complex operator -(Complex a,Complex b)
{return (Complex){a.r-b.r,a.i-b.i};}
Complex operator *(Complex a,Complex b)
{return (Complex){a.r*b.r-a.i*b.i,a.i*b.r+a.r*b.i};}
typedef Complex CD;

int n,m,l,r[N];
CD A[N],B[N];
il void fft(CD *A,int n,int opt){
    for(RG int i=0;i<n;i++)if(i<r[i])swap(A[i],A[r[i]]);
    for(RG int i=2;i<=n;i<<=1){
        RG CD w=(CD){cos(2*pi/i),opt*sin(2*pi/i)};
        for(RG int j=0;j<n;j+=i){
            RG CD wn=(CD){1,0};
            for(RG int k=j;k<j+(i>>1);k++,wn=wn*w){
                RG CD x=wn*A[k+(i>>1)];
                A[k+(i>>1)]=A[k]-x;
                A[k]=A[k]+x;
            }
        }
    }
}

int main()
{
    n=read();m=read();
    for(RG int i=0;i<=n;i++)A[i].r=read();
    for(RG int i=0;i<=m;i++)B[i].r=read();
    for(m+=n,n=1;n<=m;n<<=1)l++;
    for(RG int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));

    fft(A,n,1);fft(B,n,1);
    for(RG int i=0;i<n;i++)A[i]=A[i]*B[i];
    fft(A,n,-1);
    for(RG int i=0;i<=m;i++)printf("%d ",(int)(A[i].r/n+0.5));

    return 0;
}

NTT

补充知识

原根:

(个人理解)对于一个给定的质数\(p\),它的原根\(g\)指使得\(g^k(k=0,1,2,...,p-2)\)在\(mod\) \(p\)意义下两两不同的最小值

由于在FFT时我们需要求出\(w_2,w_4,w_8,...w_{\frac{n}{2}},w_n\),因此我们希望\(p-1\)是一个\(r\times2^k\)的形式

正好,\(998244353=119*2^{23}+1\),在\(n\leq20\)的情况下够我们用的了

主要思路

---

可以知道在FFT下的单位复根\(w_i\)就是在NTT下的\(g^{\frac{p-1}{i}}(i=2^k)\)

其他过程和FFT大致相同,其正变换的旋转向量为\(g\),逆变换的旋转向量设为\(g^{-1}\)即可

代码

与上面FFT代码不同的地方已在代码中标出

#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<iomanip>
#include<cstring>
#include<complex>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define mp make_pair
#define pb push_back
#define RG register
#define il inline
using namespace std;
typedef unsigned long long ull;
typedef vector<int>VI;
typedef long long ll;
typedef double dd;
const int mod=998244353;//模数
const int rev=332748118;//原根的逆元
const int N=3000010;
const dd eps=1e-10;
const int g=3;//原根
il ll read(){
    RG ll data=0,w=1;RG char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
    return data*w;
}

il void file(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
}

int n,m,l,r[N],A[N],B[N];
il ll poww(ll a,ll b){
    RG ll ret=1;
    for(a%=mod;b;b>>=1,a=a*a%mod)
        if(b&1)ret=ret*a%mod;
    return ret;
}

il void NTT(int *A,int n,bool opt){
    for(RG int i=0;i<n;i++)if(i<r[i])swap(A[i],A[r[i]]);
    for(RG int i=2;i<=n;i<<=1){
        RG int w=poww((opt?g:rev),(mod-1)/i);//这里变成了原根的次幂

        for(RG int j=0;j<n;j+=i){
            RG int wn=1;

            for(RG int k=j;k<j+(i>>1);k++,wn=1ll*w*wn%mod){
                RG int x=1ll*wn*A[k+(i>>1)]%mod;

                A[k+(i>>1)]=(A[k]-x+mod)%mod;
                A[k]=(A[k]+x)%mod;
            }
        }
    }
}

int main()
{
    n=read();m=read();
    for(RG int i=0;i<=n;i++)A[i]=read();
    for(RG int i=0;i<=m;i++)B[i]=read();
    for(m+=n,n=1;n<=m;n<<=1)l++;
    for(RG int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));

    NTT(A,n,1);NTT(B,n,1);
    for(RG int i=0;i<n;i++)A[i]=1ll*A[i]*B[i]%mod;
    NTT(A,n,0);

    for(RG int i=0,rv=poww(n,mod-2);i<=m;i++)
        printf("%lld ",1ll*A[i]*rv%mod);
    //这里除n的时候记得变成乘逆元
    puts("");

    return 0;
}

FFT/NTT 总结的更多相关文章

  1. FFT \ NTT总结(多项式的构造方法)

    前言.FFT  NTT 算法 网上有很多,这里不再赘述. 模板见我的代码库: FFT:戳我 NTT:戳我 正经向:FFT题目解题思路 \(FFT\)这个玩意不可能直接裸考的..... 其实一般\(FF ...

  2. [学习笔记&教程] 信号, 集合, 多项式, 以及各种卷积性变换 (FFT,NTT,FWT,FMT)

    目录 信号, 集合, 多项式, 以及卷积性变换 卷积 卷积性变换 傅里叶变换与信号 引入: 信号分析 变换的基础: 复数 傅里叶变换 离散傅里叶变换 FFT 与多项式 \(n\) 次单位复根 消去引理 ...

  3. FFT/NTT/MTT学习笔记

    FFT/NTT/MTT Tags:数学 作业部落 评论地址 前言 这是网上的优秀博客 并不建议初学者看我的博客,因为我也不是很了解FFT的具体原理 一.概述 两个多项式相乘,不用\(N^2\),通过\ ...

  4. FFT&NTT总结

    FFT&NTT总结 一些概念 \(DFT:\)离散傅里叶变换\(\rightarrow O(n^2)\)计算多项式卷积 \(FFT:\)快速傅里叶变换\(\rightarrow O(nlogn ...

  5. 快速构造FFT/NTT

    @(学习笔记)[FFT, NTT] 问题概述 给出两个次数为\(n\)的多项式\(A\)和\(B\), 要求在\(O(n \log n)\)内求出它们的卷积, 即对于结果\(C\)的每一项, 都有\[ ...

  6. FFT/NTT模板 既 HDU1402 A * B Problem Plus

    @(学习笔记)[FFT, NTT] Problem Description Calculate A * B. Input Each line will contain two integers A a ...

  7. FFT/NTT基础题总结

    在学各种数各种反演之前把以前做的$FFT$/$NTT$的题整理一遍 还请数论$dalao$口下留情 T1快速傅立叶之二 题目中要求求出 $c_k=\sum\limits_{i=k}^{n-1}a_i* ...

  8. $FFT/NTT/FWT$题单&简要题解

    打算写一个多项式总结. 虽然自己菜得太真实了. 好像四级标题太小了,下次写博客的时候再考虑一下. 模板 \(FFT\)模板 #include <iostream> #include < ...

  9. FFT&NTT数学解释

    FFT和NTT真是噩梦呢 既然被FFT和NTT坑够了,坑一下其他的人也未尝不可呢 前置知识 多项式基础知识 矩阵基础知识(之后会一直用矩阵表达) FFT:复数基础知识 NTT:模运算基础知识 单位根介 ...

  10. HDU-4609(FFT/NTT)

    HDU-4609(FFT/NTT) 题意: 给出n个木棒,现从中不重复地选出3根来,求能拼出三角形的概率. 计算合法概率容易出现重复,所以建议计算不合法方案数 枚举选出的最大边是哪条,然后考虑剩下两条 ...

随机推荐

  1. windows 下编译 OpenSSL1.0.2l 版

    1.需要的软件工具: microsoft visual studio2013(或2010以后其他版本) Perl 软件, 版本为strawberry - perl - 5.26.0.1 - 64bit ...

  2. mac攻略(4) -- 使用brew配置php7开发环境(mac+php+apache+mysql+redis)

    [http://www.cnblogs.com/redirect/p/6131751.html] 网上有很多文章都是错误的,因为是copy别人的,作者没有自己亲测,不仅不能给新手提供帮助,还会产生严重 ...

  3. Java集合中的Map接口

    jdk1.8.0_144 Map是Java三种集合中的一种位于java.util包中,Map作为一个接口存在定义了这种数据结构的一些基础操作,它的最终实现类有很多:HashMap.TreeMap.So ...

  4. git 域名配置

    在Godaddy购买的域名: 查找DNSpod解析域名,没什么难度,就是添加一条记录,保存而已,记得在添加域名到DNSpod之后,复制两个NS地址到godaddy的域名服务器下: Git项目根目录下创 ...

  5. 配置nginx服务器 —— Nginx添加多个二级子域名

    1.安装nginx centos/linux下的安装Nginx 2.安装好后进入Nginx目录中 在conf目录下建立一个vhost(ps:名字自己设定)文件夹 其中的$NGINXHOME为你的ngi ...

  6. 关于springMVC中component-scan的问题以及springmvc.xml整理

    关于springMVC中component-scan的问题以及springmvc.xml整理 一.component-scan问题和解决办法         最近在学习使用springMVC+myba ...

  7. wpf 如何让控件左右移动

    通过DoubleAnimation可以让控件进行左右移动. <Canvas x:Name="canvas_Shape" HorizontalAlignment="S ...

  8. NOIP2017 - 宝藏

    LibreOJ链接 Description 给出一个\(n(n\leq12)\)个点\(m(m\leq1000)\)条边的带权无向图,求该图的一棵生成树,使得其边权×该边距根的深度之和最小. Solu ...

  9. LOJ6000 - 「网络流 24 题」搭配飞行员

    原题链接 题意简述 求二分图的最大匹配. 题解 这里写的是匈牙利算法. 表示节点的当前匹配. 为真表示在这一轮匹配中,无法给节点一个新的匹配.所以如果为真就不用再dfs它了,直接continue就好. ...

  10. Java中子类能继承父类的私有属性吗?

    前段时间去听老师讲课的时候,老师告诉我子类是可以继承父类所有的属性和方法的.当时我是极其疑惑的,因为之前学校考试时这个考点我记得很清楚:子类只能继承父类的非私有属性和方法.老师给我的解释是这样的--先 ...