学习笔记———NTT

基本原理和FFT是一样的,那么我们参考FFT,他是把单位根代入了进去,但是这样就会产生精度问题,那么NTT如何避免这个问题呢??

我们可以用原根来代替单位根

什么是原根呢??

其实我也不知道,但是给出一个结论:一个质数P的原根为\(g\),那么\(g^1,g^2\dots g^{p-1}\)他们模P后的结果各不相同

为什么可代替呢??

考虑在FFT中我们应用了单位根哪几个性子:

  1. 对于任意的\(1\leq w \leq n\)\(W_n^k\)都各不相同

  2. \(W_n^k = W_{2n}^{2k}\)

  3. \(W_{n}^{k+\frac{n}{2}} = -W_{n}^{k}\)

根据之前给出的结论,可以证明性质1成立,然后通过一些平方运算也可以证明剩下两条性质成立,那么我们就证明了他是可替代的

剩下的就和FFT一样了,代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define ll long long
#define B cout<<"Breakpoint"<<endl;
#define O(x) cout<<#x<<" "<<x<<endl;
#define o(x) cout<<#x<<" "<<x<<" ";
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();} 
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a; 
}
const int maxn = 3e6+10,G = 3,Gi = 332748118,P = 998244353;
int dp[maxn];
ll qpow(ll a,ll k){
	ll res = 1;a = a % P;
	while (k){
		if (k&1) res = res*a % P;
		a = a*a % P;
		k >>= 1;
	} 
	return res % P;
}
int n,m,len,num;
inline void NTT(ll *a,int op){
	for (int i = 0;i < len;i++){
		if (i < dp[i]) swap(a[i],a[dp[i]]);
	}
	for (int l = 1;l < len;l <<= 1){
		ll wn = qpow(op == 1 ? G : Gi,(P-1)/(l << 1));
		for (int i = 0;i < len;i += (l <<1)){
			ll w0 = 1;
			for (int j = 0;j < l;j++,w0 = w0*wn % P){
				int x = a[i+j],y = w0*a[i+j+l] % P;
				a[i+j] = (x+y) % P,a[i+j+l] = (x-y + P) % P; 
			}
		}
	}
}
ll a[maxn],b[maxn];
int main(){
	n = read(),m = read();
	for (int i = 0;i <= n;i++) a[i] = read();
	for (int i = 0;i <= m;i++) b[i] = read();
	len = 1,num = 0;
	while (len <= n+m) len <<= 1,num++;
	for (int i = 0;i < len;i++) dp[i] = (dp[i >> 1] >> 1) | ((i & 1) << (num-1));
	NTT(a,1),NTT(b,1); 
	for (int i = 0;i < len;i++) a[i] = a[i]*b[i] % P;
	NTT(a,-1);
	ll inv = qpow(len,P-2);
	for (int i = 0;i <= n+m;i++) printf("%d ",a[i]*inv % P);
	return 0;
}
posted @ 2021-06-27 16:57  小又又yyyy  阅读(60)  评论(0编辑  收藏  举报