知識總結 多項式全家桶(三)(任意模數NTT)

2022-06-13 19:06:11 字數 3081 閱讀 6110

經過兩個月的咕咕,「多項式全家桶」 系列終於迎來了第三期……(霧)

先膜拜(伏地膜)大恐龍的部落格:任意模數 ntt

(在頁面右側面板 「您想嘴誰」 中選擇 「大恐龍」 就可以在頁面左下角戳她哦)

首先務必先學會 ntt (如果不會,請看多項式全家桶(一)),並充分理解中國剩餘定理……

之前提到了,普通 ntt 的模數必須是乙個質數,且這個質數中必須有乙個足夠大的 \(2\) 的冪作為因子。然而,畢竟滿足這樣條件的數不多,如果題目給定的模數不滿足這樣的條件呢?當然是大罵出題人毒瘤簡單來說,就是用三個滿足條件的模數 (常用的有 \(469762049\)、\(998244353\) 和 \(1004535809\)。最後將介紹如何背過它們)分別做 ntt ,然後用中國剩餘定理(下文縮寫作 crt )合併。因為如果所有係數都不超過 \(10^9\) ,項數是 \(10^5\) ,那麼最終每項係數(不取模)最大是 \(10^9\times 10^9 \times 10^5=10^\)。而 crt 合併後的數是模上述三個質數乘積(大約 \(10^\) 這個數量級)的,所以把 crt 合併後的答案模題目給定的模數即可。

然而很明顯 long long 存不下啊……於是 crt 的時候有技巧。

很明顯 crt 的時候各項獨立,於是每一項分別處理。設這一項答案是 \(x\) ,上述三個質數是 \(p_0\) 、 \(p_1\) 和 \(p_2\) ,滿足:

\[\begin x=a_0\mod p_0 \\

x=a_1 \mod p_1\\

x=a_2 \mod p_2\\

\end\]

暴力合併是會爆 long long 的,不過可以先合併前兩項( \(p_0\times p_1<2^\)),得到(\(\mathrm(x,y)\) 表示 \(x\) 在模 \(y\) 意義下的逆元):

\[x=a_0p_1\mathrm(p_1, p_0)+a_1p_0\mathrm(p_0, p_1)\mod p_0p_1

\]把右邊那一堆設為 \(t_0\) (模仿恐龍的變數名 qwq ),設答案為 \(t_1p_0p_1+t_0\) ,則有:

\[t_1p_0p_1+t_0=a_2\mod p_2

\]即:

\[t_1=(a_2-t_0)\mathrm(p_0p_1, p_2)\mod p_2

\]因為 \(x,所以 \(t_1,所以求出來直接就是真正的 \(t_1\) 。然後在模題目給定的模數意義下算 \(t_1p_0p_1+t_0\) 就行了。

我是在機房裡以被兔崽子揍一頓的代價把這三個數字吼了一上午並且用安徽黃梅戲《天仙配》的曲調唱了一晚上才背過

先看一眼要記住的東西:

469762049、998244353、1004535809

記不住?跟我大聲念:

肆陸玖柒陸貳零肆玖!

玖玖捌貳肆肆叄伍叄!

壹零零肆伍叄伍捌零玖!

沒記住?再來一遍:

肆陸玖柒陸貳零肆玖!

玖玖捌貳肆肆叄伍叄!

壹零零肆伍叄伍捌零玖!

還沒記住?再吼十遍

在頁面右側面板點一下 「運動員進行曲」 ,然後跟著唱:

(前奏:噹噹噹噹噹噹噹噹噹噹噹噹當!)

肆陸玖柒陸啊貳零~~肆玖~~

肆陸陸玖柒陸啊貳零肆玖~

壹零零零零肆伍叄伍~捌!零!玖!

玖玖捌貳肆肆肆肆肆叁叄叄~叄伍叄!

(以上重複一遍)

肆陸玖柒陸貳零肆玖~~

玖玖捌貳肆肆叄伍叄~~

壹零零~啊肆伍叄伍捌~零玖

玖玖捌貳肆肆叄伍叄~~

(以上重複一遍)

注意在模 \(p_0p_1\) 意義下算乘法的時候要用龜速乘防止爆 long long 。這裡介紹一種從恐龍那裡學來的

\(o(1)\) 龜速乘:

inline ll mul(ll a, ll b, const ll p)

原理是利用了 long long 溢位後不是隨機數,而只是真實值減去了 \(2^\) ,所以 \(a\) 和 \(\lfloor\frac\rfloor\cdot b\) 之差仍然是 \(a\mod b\)。

完整**(洛谷4245):

#include #include #include #include using namespace std;

namespace zyt

templateinline void write(t x)

typedef long long ll;

const int n = 1e5 + 10, len = n << 2, p = ;

int n;

inline ll mul(ll a, ll b, const ll p)

inline ll power(ll a, ll b, const ll p)

return ans;

} ll inv(const ll a, const ll p)

namespace polynomial

}void init(const int n, const int lg2)

for (int i = 0; i < n; i++)

rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg2 - 1));

} void mul(const int *a, const int *b, int *c, const int n, const int m, const int _p)

}int a[n], b[n], ans[4][len];

void crt(const int n, const int mod) }

int work() }

int main()

多項式全家桶

眾所周知,生成函式是乙個十分強大的東西,許多與多項式相關的演算法也就應運而生了,在這裡選取幾種較為簡單的演算法做乙個介紹.p.s.這篇文章在去年noi前已經完成了一半,現在筆者將其補充完整後發出,同時也為了紀念那一段美好的時光。已知 f x 求 g x 使得 f x g x equiv 1 mod ...

多項式全家桶

已知多項式 g x 求 f x 滿足 g f x equiv 0 pmod 假設我們有乙個 f 0 x 滿足 g f 0 x equiv 0 pmod rceil 由定義可知 f x f 0 x equiv 0 pmod rceil rightarrow forall k ge 2,left f x...

多項式全家桶

include include includeusing namespace std const double pi acos 1.0 const int maxn 1e7 5 inline int read int n,m,len,lim 1 int r maxn struct complex c...