NTT快速数论变换

NTT

理解了FFT的原理,NTT也差不多。FTT是用复数实现变换,而NTT是用取模意义实现。
找出一个g,和开一个模数p,gp的原根。

原根 $0<i<P,0<j<P,1<g<P\ g^i(mod\ p)\ne g^j(mod\ p)$

这样在FTT里面的$w_n^1\equiv g^{\frac{p-1}{n}}$。
显然:
$(g^\frac{p-1}{n})^n=g^{p-1}=1(mod\ p)$
$g^\frac{p-1}{n} * g^\frac{p-1}{n} = g^{2\frac{p-1}{n}}$
同样的 FFT 里面用到的几个原根性质,他都有。
你可以抽象为,把一个长度为 P 线段,每次走一格,走了N次回来了。


所以一开始要求的几个点值是
$\{A(1),A(g^{\frac{p-1}{n}}),\cdots,A(g^{\frac {p-1}{n}{n-1}})\}$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//ntt
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=(1<<18)+5, INF=1e9;
const double PI=acos(-1);
inline int read(){
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}

ll P=1004535809;
ll Pow(ll a, ll b,ll P) {
ll ans=1;
for(; b; b>>=1, a=a*a%P)
if(b&1) ans=ans*a%P;
return ans;
}
struct NumberTheoreticTransform {
int pow2(int x) {
int res = 1;
while (res < x)
res <<= 1;
return res;
}


inline LL pow_mod(ll x, int n) {
ll res;
for (res = 1; n; n >>= 1, x = x * x % mod)
if (n & 1)
res = res * x % mod;
return res;
}

inline int add_mod(int x, int y) {
x += y;
return x >= mod ? x - mod : x;
}

inline int sub_mod(int x, int y) {
x -= y;
return x < 0 ? x + mod : x;
}

void NTT(LL a[], int n, int op) {
for (int i = 1, j = n >> 1; i < n - 1; ++i) {
if (i < j)
swap(a[i], a[j]);
int k = n >> 1;
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
for (int len = 2; len <= n; len <<= 1) {
LL g = pow_mod(3, (mod - 1) / len);
for (int i = 0; i < n; i += len) {
LL w = 1;
for (int j = i; j < i + (len >> 1); ++j) {
LL u = a[j], t = 1ll * a[j + (len >> 1)] * w % mod;
a[j] = (u + t) % mod, a[j + (len >> 1)] = (u - t + mod) % mod;
w = 1ll * w * g % mod;
}
}
}
if (op == -1) {
reverse(a + 1, a + n);
LL inv = pow_mod(n, mod - 2);
for (int i = 0; i < n; ++i)
a[i] = 1ll * a[i] * inv % mod;
}
}

void mul(LL A[], LL B[], int Asize, int Bsize) {
int n = pow2(Asize + Bsize - 1);
for (int i = Asize; i < n; ++i)
A[i] = 0;
for (int i = Bsize; i < n; ++i)
B[i] = 0;
NTT(A, n, 1);
NTT(B, n, 1);
for (int i = 0; i < n; ++i) {
A[i] = 1ll * A[i] * B[i] % mod;
B[i] = 0;
}
NTT(A, n, -1);
return;
}
} f;

int n1, n2, m, c[N];
ll a[N], b[N];
char s1[N], s2[N];
int main() {
//freopen("in","r",stdin);
scanf("%s%s",s1,s2);
n1=strlen(s1); n2=strlen(s2);
for(int i=0; i<n1; i++) a[i] = s1[n1-i-1]-'0';
for(int i=0; i<n2; i++) b[i] = s2[n2-i-1]-'0';
m=n1+n2-1;
f.mul(a, b, m);
for(int i=0; i<m; i++) c[i]=a[i];
for(int i=0; i<m; i++) c[i+1]+=c[i]/10, c[i]%=10;
if(c[m]) m++;
for(int i=m-1; i>=0; i--) printf("%d",c[i]);
}