[多项式全家桶]FFT/NTT

开始补EC PK赛的题目,在此之前决定先完善多项式全家桶的学习任务。


FFT

O ( l o g N ) O(logN) O(logN)的时间完成点值、系数的多项式表示,在 O ( N ) O(N) O(N)的时间完成多项式乘积。

FFT在之前学习过:[多项式全家桶] 快速傅立叶变换(FFT)
再推荐一篇博客:FFT\NTT总结

在学习别的知识点之前又自己结合原理撸了一遍fft代码,贴一下符合自己习惯(比如从0开始的计数)的代码:

//逆fft取op==-1是因将范德蒙德行列式的逆时每一项取共轭再除以n
void fft(COM *a, int op){
    for(int i = 0; i < N; i++) if(i < inv[i]) swap(a[inv[i]], a[i]);
    
    for(int i = wei - 1; i >= 0; i--){
	    //d表示第i层每一小段的长度
        int d = 1 << (wei - i);				
        //wn是当前层下使用的单位根
        COM wn = {cos(2 * pi / d), op * sin(2 * pi / d)};
        
        //j遍历第i层的每段开头,更新每个d小段
        for(int j = 0; j < N; j += d){
			//在一个小段中新设一个量w进行更新操作
            COM w = {1, 0};
            for(int k = 0; k < d / 2; k++){
                COM x, y; 
                x = a[j + k]; y = a[j + k + d / 2] * w;
                a[j + k] = x + y; a[j + k + d / 2] = x - y;
                w = w * wn;
            }
        }
    }
}

注意,如果对多项式A和B进行FFT求乘积时,A、B数组的大小都要开至maxn << 2!!


NTT

在此之前,需要学习一下原根的知识点。

参考博客:HolseLee

定义为:设 m > 1 , g c d ( a , m ) = 1 , m>1,gcd(a,m)=1, m>1,gcd(a,m)=1,那么使得 a r ≡ 1 ( m o d a^r≡1(mod ar1(mod m ) m) m)成立的最小正整数称为 a a a对模 m m m的阶,记为 δ m ( a ) \delta_m(a) δm(a)

易知阶有两个相关定理:

  • 若满足 m > 1 , g c d ( a , m ) = 1 m>1,gcd(a,m)=1 m>1,gcd(a,m)=1,并且 a n ≡ 1 ( m o d a^n≡1(mod an1(mod m ) m) m),则有 δ m ( a ) \delta_m(a) δm(a) | n . n. n.
  • δ m ( a ) \delta_m(a) δm(a) | ϕ ( m ) . \phi(m). ϕ(m).

进而,定义原根:设 a a a是整数, m m m是正整数,如果 δ m ( a ) = ϕ ( m ) \delta_m(a) =\phi(m) δm(a)=ϕ(m),那么称 a a a m m m的一个原根。

原根有三个相关定理:

  • 一个正整数 m m m有原根的充要条件是 m = 2 , 4 , p e , 2 p e m=2,4,p^e,2p^e m=2,4,pe,2pe,其中 p p p是奇素数, e e e是正整数。
    证明略
  • p p p m m m的一个原根,则 p , p 2 , p 2 , … , p ϕ ( m ) p,p^2,p^2,\dots,p^{\phi(m)} p,p2,p2,,pϕ(m)各数对 m m m取模的最小剩余就是小于 m m m且与 m m m互素的 ϕ ( m ) \phi(m) ϕ(m)个数的一个排列。
    证明: 易知任意两个元素均不相同,因而接下来只需要证明每个元素都与 m m m互素。
    p ϕ ( m ) ≡ 1 ( m o d   m ) p^{\phi(m)}≡1(mod\space m) pϕ(m1(mod m)可知 ( p i ) ϕ ( m ) ≡ 1 ( m o d   m ) (p^i)^{\phi(m)}≡1(mod \space m) (pi)ϕ(m)1(mod m)因而我们只需要证明如果某个数 a a a m m m的阶存在,那么就有 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1.为此,我们假设 a i ≡ 1 ( m o d   m ) a^i≡1(mod \space m) ai1(mod m)且由 g c d ( a , m ) = d gcd(a,m)=d gcd(a,m)=d.所以有 a i = k m + 1 a^i=km+1 ai=km+1
    其中 a i a^i ai k m km km均可以被 d d d整除,因而1也必须被d整除,故 d ∣ 1 d \mid1 d1 d = 1. d=1. d=1.
  • 对每一个正整数 m m m都有 ϕ ( ϕ ( m ) ) \phi(\phi(m)) ϕ(ϕ(m))个原根,特别地,素数 p p p ϕ ( p − 1 ) \phi(p-1) ϕ(p1)个原根。
    证明: p p p m m m的一个原根,那么由上述定理其既约剩余系取遍小于 m m m且与 m m m互素的所有数。在 1 、 2 、 … 、 ϕ ( m ) 1、2、\dots、\phi(m) 12ϕ(m)中只有那些与 ϕ ( m ) \phi(m) ϕ(m)互素的幂次才可以成为原根,这是因为那些与 ϕ ( m ) \phi(m) ϕ(m)不互素的元素产生的 ϕ ( m ) \phi(m) ϕ(m)个元素是某些元素集合的 d d d次重复,其中 d = g c d ( p i , ϕ ( m ) ) . d= gcd(p^i,\phi(m)). d=gcd(pi,ϕ(m)).

原根的求法?
首先将 ϕ ( m ) \phi(m) ϕ(m)做标准素数分解: ϕ ( m ) = p 1 e 1 ∗ p 2 e 2 ∗ ⋯ ∗ p k e k \phi(m)=p_1^{e_1}*p_2^{e_2}*\dots*p_k^{e_k} ϕ(m)=p1e1p2e2pkek
然后枚举 g g g,若恒满足 g ϕ ( m ) p i ≠ 1 ( m o d   m ) ,   i = 1 , 2 , … , k g^{\frac{\phi(m)}{p_i}}≠1(mod\space m),\space i=1,2,\dots,k gpiϕ(m)=1(mod m), i=1,2,,k
g g g m m m的一个原根。

证明可以从阶的第二个定理出发。因为 δ m ( a ) \delta m(a) δm(a)总是 ϕ ( m ) \phi(m) ϕ(m)的一个因子,所以若 δ m ( a ) \delta m(a) δm(a)小于 ϕ ( m ) \phi(m) ϕ(m),则 δ m ( a ) \delta m(a) δm(a)一定会小等于某一个 ϕ ( m ) p i \frac{\phi(m)}{p_i} piϕ(m),从而 g ϕ ( m ) p i g^{\frac{\phi(m)}{p_i}} gpiϕ(m) m m m的值一定为1.

下面正式学习一下NTT(快速数论变换)

参考博客:快速数论变换(NTT)小结

类似于FFT中我们寻找的 w n w_n wn,我们在模 M M M下利用原根做循环。

因为我们采取二分递归的办法解决点值、系数转化问题,所以我们希望找一些具有优良的特殊性质的素数 M = k ∗ 2 r + 1 M=k*2^r+1 M=k2r+1作为模数。(参考:FFT用到的各种素数)
比如: 119 < < 23 + 1 119<<23+1 119<<23+1可以处理 1 e 6 1e6 1e6的数据, 17 < < 27 + 1 17<<27+1 17<<27+1平方后不会爆longlong. 这两个数字对应的原根均为3.

之后,我们构造循环: w n = p m − 1 n w_n=p^{\frac{m-1}{n}} wn=pnm1
由原根性质, w n , w n 2 , … , w n n w_n,w_n^2,\dots,w_n^n wn,wn2,,wnn构成一个 n n n阶循环群,满足复数 w n = e 2 π n w_n=e^{\frac{2 \pi }{n}} wn=en2π的对应性质。
所以在求做INNT的时候 p p p的逆作为逆矩阵的元素,最后在输出答案的时候除以 n n n,即乘上 n n n的逆

核心代码与FFT类似,仅需要在必要处取模&更改对应的循环因子 w n w_n wn即可。


暂且先复习FFT和学习NTT,之后更多的多项式算法日后再学。

ywwyww
DCDCBigBig

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值