任意模數ntt 任意模數NTT

2021-10-17 06:02:51 字數 3141 閱讀 8225

任意模數\(ntt\)

眾所周知,為了滿足單位根的性質,\(ntt\)需要質數模數,而且需要能寫成\(a2^ + 1\)且\(2^k \ge n\)

比較常用的有\(998244353,1004535809,469762049\),這三個原根都是\(3\)

如果要任意模數怎麼辦?

\(n\)次多項式在模\(m\)下乘積,最終係數一定不會大於\(nm^2\)

所以我們找三個模數分別做\(ntt\)再合併一下就好辣

但這樣的合併結果會爆\(long long\)呢

需要用高精嗎?

可以使用一些技巧

我們要合併的是

\[\left \

x \equiv a_1 \pmod \\

x \equiv a_2 \pmod \\

x \equiv a_3 \pmod \\

\end

\right.

我們先在\(long long\)範圍內合併前兩個

\[\left \

x \equiv a \pmod m \\

x \equiv a_3 \pmod \\

\end

\right.

由於最後結果模\(m\)為\(a\),模\(m_3\)為\(a_3\)

設最後的答案是

\[ans = km + a

且\(k\)需要滿足

\[km + a \equiv a_3 \pmod

所以\(k\)一定是在模\(m_3\)意義下求出的,為

\[k \equiv (a_3 - a)m^ \pmod

求出\(k\)後就可以直接在原模數意義下求出

\[ans = km + a

在第一次合併的時候需要快速乘

做三次\(ntt\)常數有夠大的

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define ll long long int

#define rep(i,n) for (int i = 1; i <= (n); i++)

#define redge(u) for (int k = h[u],to; k; k = ed[k].nxt)

#define cls(s,v) memset(s,v,sizeof(s))

#define mp(a,b) make_pair(a,b)

#define cp pair

using namespace std;

const int maxn = 400005,maxm = 100005,inf = 0x3f3f3f3f;

inline int read()

while (c >= 48 && c <= 57)

return flag ? out : -out;

int pr=;

int r[maxn];

inline ll qpow(ll a,ll b,ll p)fft[3];

int f[maxn],g[maxn],b[maxn],deg1,deg2,deg,md;

ll ans[maxn];

ll inv(ll n,ll p)

ll mul(ll a,ll b,ll p){

ll re = 0;

for (; b; b >>= 1,a = (a + a) % p)

if (b & 1) re = (re + a) % p;

return re;

void crt(){

deg = deg1 + deg2;

ll a,b,c,t,k,m = 1ll * pr[0] * pr[1];

ll inv1 = inv(pr[1],pr[0]),inv0 = inv(pr[0],pr[1]),inv3 = inv(m % pr[2],pr[2]);

for (int i = 0; i <= deg; i++){

a = fft[0].a[i],b = fft[1].a[i],c = fft[2].a[i];

t = (mul(a * pr[1] % m,inv1,m) + mul(b * pr[0] % m,inv0,m)) % m;

k = ((c - t % pr[2]) % pr[2] + pr[2]) % pr[2] * inv3 % pr[2];

ans[i] = ((k % md) * (m % md) % md + t % md) % md;

void conv(){

int n = 1,l = 0;

while (n <= (deg1 + deg2)) n <<= 1,l++;

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

for (int u = 0; u <= 2; u++){

fft[u].g = 3; fft[u].p = pr[u];

for (int i = 0; i <= deg1; i++) fft[u].a[i] = f[i];

for (int i = 0; i <= deg2; i++) b[i] = g[i];

for (int i = deg2 + 1; i < n; i++) b[i] = 0;

fft[u].ntt(fft[u].a,n,1); fft[u].ntt(b,n,1);

for (int i = 0; i < n; i++) fft[u].a[i] = 1ll * fft[u].a[i] * b[i] % pr[u];

fft[u].ntt(fft[u].a,n,-1);

int main(){

deg1 = read(); deg2 = read(); md = read();

for (int i = 0; i <= deg1; i++) f[i] = read();

for (int i = 0; i <= deg2; i++) g[i] = read();

conv(); crt();

for (int i = 0; i <= deg; i++) printf("%lld ",ans[i]);

return 0;

任意模數ntt 模板篇 NTT和三模數NTT

之前寫過fft的筆記.我們知道fft是在複數域上進行的變換.而且經過數學家的證明,dft是複數域上唯一滿足迴圈卷積性質的變換.而我們在oi中,經常遇到對 x取模的題目,這就啟發我們可不可以在模運算的意義下找乙個這樣的變換.然後我們發現有個神奇的東西,原根 g 這東西在模意義下相當於單位復根 e 所以...

任意模數NTT學習筆記

這兩天有點頹,所以東西學的也很慢。這個一眼就能推出來的活生生卡了我兩天。說幾個細節 柿子 m 通常設定為 32768 把上一步的幾個韓束化成 a,b,c,d 的形式,答案就是 一看卷積,多搞幾次 fft 就過去了。陣列記得開大。n 2 左右。include using namespace std d...

任意模數NTT 拆係數FFT

給定 2 個多項式 f x g x 請求出 f x g x 係數對 p 取模,且不保證 p 可以分解成 p a cdot 2 k 1 之形式。不關心 1 leq n leq 10 5,0 leq a i,b i leq 10 9,2 leq p leq 10 9 9 現在這裡有兩個多項式 a x b...