任意模數\(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...