22717 高翊恩
a.k.a. 數學
沒事 今天沒有幾何
\(\bold{v}^T=\left[\begin{array}{cccc}2&3&-5&7\end{array}\right]\)
\(\bold{v}=\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]\)
把對應位置相乘後加總
\(\bold{v}^T=\left[\begin{array}{cccc}2&3&-5&7\end{array}\right]\)
\(\bold{u}^T=\left[\begin{array}{cccc}4&2&0&-1\end{array}\right]\)
\(\bold{v}^T\cdot\bold{u}^T=2\cdot4+3\cdot2+(-5)\cdot0+7\cdot(-1)=7\)
\(\left[\begin{array}{cccc}2&4&0&-1\\3&5&2&-5\\-4&6&5&0\end{array}\right]\)
\(\left[\begin{array}{cccc}2&4&0&-1\\3&5&2&-5\\-4&6&5&0\\3&-2&0&4\end{array}\right]\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]=\left[\begin{array}{c}9\\-24\\-15\\28\end{array}\right]\)
\(\left[\begin{array}{cccc}2&4&0&-1\\3&5&2&-5\\-4&6&5&0\\3&-2&0&4\end{array}\right]\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]=\left[\begin{array}{c}9\\-24\\-15\\28\end{array}\right]\)
\(\left[\begin{array}{cccc}2&4&0&-1\\3&5&2&-5\\-4&6&5&0\\3&-2&0&4\end{array}\right]\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]=\left[\begin{array}{c}9\\-24\\-15\\28\end{array}\right]\)
\(\left[\begin{array}{cccc}2&4&0&-1\\3&5&2&-5\\-4&6&5&0\\3&-2&0&4\end{array}\right]\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]=\left[\begin{array}{c}9\\-24\\-15\\28\end{array}\right]\)
\(\left[\begin{array}{cccc}2&4&0&-1\\3&5&2&-5\\-4&6&5&0\\3&-2&0&4\end{array}\right]\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]=\left[\begin{array}{c}9\\-24\\-15\\28\end{array}\right]\)
\(A(x)=2+3x-5x^2+7x^3\)
\(\bold{A}=\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]\)
\(A(x)=2+3x-5x^2+7x^3\)
\(A(-3)=2+3\cdot(-3)-5\cdot(-3)^2+7\cdot(-3)^3=-5\)
\(\left[\begin{array}{cccc}(-3)^0&(-3)^1&(-3)^2&(-3)^3\end{array}\right]\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]=-5\)
長得很像做內積
\(A(x)=2+3x-5x^2+7x^3\)
\(\left[\begin{array}{cccc}(-3)^0&(-3)^1&(-3)^2&(-3)^3\\2^0&2^1&2^2&2^3\\0^0&0^1&0^2&0^3\end{array}\right]\left[\begin{array}{c}2\\3\\-5\\7\end{array}\right]=\left[\begin{array}{c}-5\\25\\2\end{array}\right]\)
\(\operatorname{cis}(\theta)=e^{i\theta}=\cos\theta+i\sin\theta\)
事先說好
\(1\)的\(n\)次方根中,由\(x\)軸正向開始逆時針掃,掃到的第\(k\)個解,稱為\(\omega_n^k\)
\(\omega_n^k=\operatorname{cis}(2\pi\cdot\frac{k}{n})\)
給你兩個\(n\)次多項式,請輸出它們相乘的結果
複雜度限制\(\Omicron(n^2)\)
給你兩個\(n\)次多項式,請輸出它們相乘的結果
複雜度限制\(\Omicron(n\log n)\)
有兩個未知的多項式\(A(x)\)和\(B(x)\)和一個長度為\(n\)的已知陣列\(x\),已知
\(\left\{\begin{array}{l}A(x_i)=\alpha_i\\B(x_i)=\beta_i\end{array}\right.,1\leq i\leq n\)
令
\(c_i=(A*B)(x_i)\ ,1\leq i\leq n\)
求\(c\)
\(c_i=\alpha_i\cdot\beta_i\)
hehe
給你兩個\(n\)次多項式,請輸出它們相乘的結果
複雜度限制\(\Omicron(n\log n)\)
挑一堆數字\(x_i\)給\(A\)和\(B\),就會有\(\alpha_i\)和\(\beta_i\)
令\(c_i=\alpha_i\cdot\beta_i\),就可以得到\((A*B)(x_i)\)
這相當於是\((A*B)(x)\)函數圖形上的一堆點,想辦法做一些奇怪的數學操作就可以把它變回來了
\(A(x)=x-1\)
\(B(x)=-x+2\)
\(A(x)=x-1\)
\(B(x)=-x+2\)
\((0,-1)\)
\((1,0)\)
\((0,2)\)
\((1,1)\)
\(A(x)=x-1\)
\(B(x)=-x+2\)
\((0,-1)\)
\((1,0)\)
\((0,2)\)
\((1,1)\)
\(A(x)=x-1\)
\(B(x)=-x+2\)
\((0,-1)\)
\((1,0)\)
\((0,2)\)
\((1,1)\)
這是\(A*B\)嗎?
\(A(x)=x-1\)
\(B(x)=-x+2\)
\((0,-1)\)
\((1,0)\)
\((2,1)\)
\((0,2)\)
\((1,1)\)
\((2,0)\)
\(A(x)=x-1\)
\(B(x)=-x+2\)
\((0,-1)\)
\((1,0)\)
\((2,1)\)
\((0,2)\)
\((1,1)\)
\((2,0)\)
\(A(x)=x-1\)
\(B(x)=-x+2\)
\((0,-1)\)
\((1,0)\)
\((2,1)\)
\((0,2)\)
\((1,1)\)
\((2,0)\)
兩個次數為\((n-1)\)的多項式相乘,至少要帶多少個數字進去?
至少要帶幾個點才能決定唯一一個次數為\(n\)的多項式?
\(A(x),B(x)\)
\(\alpha,\beta\)
\(c\)
\((A*B)(x)\)
帶值進去
兩兩相乘
想辦法弄回來
令\(\bold{M}=\left[\begin{array}{ccccc}x_0^0&x_0^1&x_0^2&\cdots&x_0^{n-1}\\x_1^0&x_1^1&x_1^2&\cdots&x_1^{n-1}\\x_2^0&x_2^1&x_2^2&\cdots&x_2^{n-1}\\\vdots&\vdots&\vdots&\ddots&\vdots\\x_{n-1}^0&x_{n-1}^1&x_{n-1}^2&\cdots&x_{n-1}^{n-1}\end{array}\right]\)
則\(\left\{\begin{array}{l}\bold{MA}=\bold{\alpha}\\\bold{MB}=\bold{\beta}\end{array}\right.\)
就兩兩相乘阿
我們可以找出\(\bold{M}^{-1}\)
則\(\bold{M}^{-1}\bold{c}=\bold{A}*\bold{B}\)
\(A(x),B(x)\)
\(\alpha,\beta\)
\(c\)
\((A*B)(x)\)
乘一個矩陣
兩兩相乘
乘一個反矩陣
\(A(x),B(x)\)
\(\alpha,\beta\)
\(c\)
\((A*B)(x)\)
\(\Omicron(?)\)
\(\Omicron(n)\)
\(\Omicron(?)\)
如果我們可以讓「乘一個矩陣」在\(\Omicron(n\log n)\)的時間做完,
就有辦法在\(\Omicron(n\log n)\)的時間內將兩個多項式相乘!
DFT
理論上我們要帶\(2n\)個數字進去
多項式帶值是\(\Omicron(n)\)
有沒有更快的做法?
於是我們就想到了 Divide and Conquer
帶\(\omega_N^0,\omega_N^1,\omega_N^2,\dots,\omega_N^{N-1}\)這\(N\)個數字!
※溫馨提醒:\(N\)要是一個比\(2n\)大的「\(2\)的冪次」
\(\bold{\Omega}_N\bold{A}=\operatorname{DFT}(\bold{A})=\bold{Y}\)
我們的目標是找出\(\bold{Y}\)
成功的話就是 Fast Fourier Transform
若\(N=1\),則\(\bold{\Omega}_N\bold{A}=\bold{A}\)
否則:
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
到這裡已經好一半了
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
(若\(0\leq k<n\))
\(y_{0,k},y_{1,k}皆已知\)
萬歲!
typedef complex<double> cd;
cd cis(double theta) {
return complex<double>(cos(theta), sin(theta));
}
cd omega(int n, int k) {
return cis(acos(-1) * 2 / n * k);
}
vector<cd> FFT(vector<cd> &A) {
int N = A.size();
// N is the power of 2
if (N == 1) return vector<cd>(1, A[0]);
int n = N / 2;
vector<cd> A0(n), A1(n);
for (int i = 0; i < n; i++) {
A0[i] = A[i * 2];
A1[i] = A[i * 2 + 1];
}
vector<cd> Y0 = FFT(A0), Y1 = FFT(A1);
vector<cd> Y(N);
for (int k = 0; k < n; k++) {
Y[k] = Y0[k] + omega(N, k) * Y1[k];
Y[k + n] = Y0[k] - omega(N, k) * Y1[k];
}
return Y;
}
typedef complex<double> cd;
cd cis(double theta) {
return complex<double>(cos(theta), sin(theta));
}
cd omega(int n, int k) {
return cis(acos(-1) * 2 / n * k);
}
void FFT(vector<cd> &A) {
int N = A.size();
// N is the power of 2
if (N == 1) return;
int n = N / 2;
vector<cd> A0(n), A1(n);
for (int i = 0; i < n; i++) {
A0[i] = A[i * 2];
A1[i] = A[i * 2 + 1];
}
FFT(A0), FFT(A1);
for (int k = 0; k < n; k++) {
A[k] = A0[k] + omega(N, k) * A1[k];
A[k + n] = A0[k] - omega(N, k) * A1[k];
}
}
\(a_0\)
\(a_1\)
\(a_2\)
\(a_3\)
\(a_4\)
\(a_5\)
\(a_6\)
\(a_7\)
]
\(a_0\)
\(a_1\)
\(a_2\)
\(a_3\)
\(a_4\)
\(a_5\)
\(a_6\)
\(a_7\)
[
[
]
[
]
\(a_0\)
\(a_4\)
\(a_2\)
\(a_6\)
\(a_1\)
\(a_5\)
\(a_3\)
\(a_7\)
[
[
[
[
]
]
]
]
\(a_0\)
\(a_4\)
\(a_2\)
\(a_6\)
\(a_1\)
\(a_5\)
\(a_3\)
\(a_7\)
[
]
[
[
[
[
[
[
[
]
]
]
]
]
]
]
\(y_0\)
\(y_1\)
\(y_2\)
\(y_3\)
\(y_4\)
\(y_5\)
\(y_6\)
\(y_7\)
]
[
\(a_0\)
\(a_1\)
\(a_2\)
\(a_3\)
\(a_4\)
\(a_5\)
\(a_6\)
\(a_7\)
]
[
\(a_0\)
\(a_4\)
\(a_2\)
\(a_6\)
\(a_1\)
\(a_5\)
\(a_3\)
\(a_7\)
[
]
[
[
[
[
[
[
[
]
]
]
]
]
]
]
\(y_0\)
\(y_1\)
\(y_2\)
\(y_3\)
\(y_4\)
\(y_5\)
\(y_6\)
\(y_7\)
]
[
\(a_0\)
\(a_1\)
\(a_2\)
\(a_3\)
\(a_4\)
\(a_5\)
\(a_6\)
\(a_7\)
]
[
\(a_0\)
\(a_4\)
\(a_2\)
\(a_6\)
\(a_1\)
\(a_5\)
\(a_3\)
\(a_7\)
[
]
[
[
[
[
[
[
[
]
]
]
]
]
]
]
\(y_0\)
\(y_1\)
\(y_2\)
\(y_3\)
\(y_4\)
\(y_5\)
\(y_6\)
\(y_7\)
]
[
奇怪排序 \(\longrightarrow\) 一段一段合併
\([\)
\(4\)
\(0\)
\(2\)
\(-5\)
\(-2\)
\(6\)
\(3\)
\(-1\)
\(]\)
\([\)
\(4\)
\(0\)
\(2\)
\(-5\)
\(-2\)
\(6\)
\(3\)
\(-1\)
\(]\)
\(0\)
\(1\)
\(2\)
\(3\)
\(4\)
\(5\)
\(6\)
\(7\)
\([\)
\(4\)
\(0\)
\(2\)
\(-5\)
\(-2\)
\(6\)
\(3\)
\(-1\)
\(]\)
\(0\)
\(1\)
\(2\)
\(3\)
\(4\)
\(5\)
\(6\)
\(7\)
\([\)
\(4\)
\(0\)
\(2\)
\(-5\)
\(-2\)
\(6\)
\(3\)
\(-1\)
\(]\)
\([\)
\(4\)
\(0\)
\(2\)
\(-5\)
\(-2\)
\(6\)
\(3\)
\(-1\)
\(]\)
\([\)
\(2\)
\(0\)
\(2\)
\(-5\)
\(6\)
\(6\)
\(3\)
\(-1\)
\(]\)
\([\)
\(2\)
\(0\)
\(2\)
\(-5\)
\(6\)
\(6\)
\(3\)
\(-1\)
\(]\)
\([\)
\(2\)
\(0\)
\(5\)
\(-5\)
\(6\)
\(6\)
\(-1\)
\(-1\)
\(]\)
\([\)
\(2\)
\(0\)
\(5\)
\(-5\)
\(6\)
\(6\)
\(-1\)
\(-1\)
\(]\)
\([\)
\(2\)
\(6\)
\(5\)
\(-5\)
\(6\)
\(-6\)
\(-1\)
\(-1\)
\(]\)
\([\)
\(2\)
\(6\)
\(5\)
\(-5\)
\(6\)
\(-6\)
\(-1\)
\(-1\)
\(]\)
\([\)
\(2\)
\(6\)
\(5\)
\(-6\)
\(6\)
\(-6\)
\(-1\)
\(-4\)
\(]\)
\([\)
\(2\)
\(6\)
\(5\)
\(-6\)
\(6\)
\(-6\)
\(-1\)
\(-4\)
\(]\)
\([\)
\(2\)
\(6\)
\(5\)
\(-6\)
\(6\)
\(-6\)
\(-1\)
\(-4\)
\(]\)
\([\)
\(6\)
\(-6\)
\(-6\)
\(-4\)
\(]\)
\(6-i\)
\(6+i\)
\(-3\)
\(7\)
\([\)
\(6\)
\(-6\)
\(-6\)
\(-4\)
\(]\)
\(6-i\)
\(6+i\)
\(-3\)
\(7\)
\([\)
\(]\)
\(6-i\)
\(6+i\)
\(-3\)
\(7\)
\(0\)
\(-6-4i\)
\(12\)
\(-6+4i\)
\([\)
\(]\)
\(6-i\)
\(6+i\)
\(-3\)
\(7\)
\(0\)
\(-6-4i\)
\(12\)
\(-6+4i\)
\([\)
\(]\)
\(6-i\)
\(6+i\)
\(-3\)
\(7\)
\(0\)
\(-6-4i\)
\(12\)
\(-6+4i\)
\([\)
\(]\)
\(6-i\)
\(6+i\)
\(-3\)
\(7\)
\(0\)
\(-6-4i\)
\(12\)
\(-6+4i\)
自己做
void SORT(int *a, int N);
void CONQUER(pii y0_range, pii y1_range);
void FFT(int *a, int N) {
SORT(a, N);
for (int w = 1; w < N; w <<= 1) {
// w: the width of subarrays that hasn't been conquered
for (int s = 0; s < N; s += w * 2) {
// s: the start position of the first array to be conquered
CONQUER({s, s + w}, {s + w, s + w * 2});
}
}
}
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
\(000\)
\(001\)
\(010\)
\(011\)
\(100\)
\(101\)
\(110\)
\(111\)
不喜歡\(\operatorname{cis}\)
0,0 | 0,1 |
---|
1,0 | 1,1 |
---|
0 | 1 | 2 | 3 |
---|
0,0 | 0,1 |
---|
1,0 | 1,1 |
---|
0 | 1 | 2 | 3 |
---|
0,0 | 0,1 |
---|
1,0 | 1,1 |
---|
0 | 1 | 2 | 3 |
---|
0,0 | 0,1 |
---|
1,0 | 1,1 |
---|
0 | 1 | 2 | 3 |
---|
所有紅色的地方都要搭配\(\omega_4^0\)
所有綠色的地方都要搭配\(\omega_4^1\)
0,0 | 0,1 |
---|
1,0 | 1,1 |
---|
0 | 1 | 2 | 3 |
---|
0,0 | 0,1 |
---|
1,0 | 1,1 |
---|
0 | 1 | 2 | 3 |
---|
比起分開 Conquer
不如依序把\(\omega\)丟進去算!
0,0 | 0,1 |
---|
1,0 | 1,1 |
---|
0 | 1 | 2 | 3 |
---|
0,0 | 0,1 |
---|
1,0 | 1,1 |
---|
0 | 1 | 2 | 3 |
---|
\(\Rightarrow\) 每次操作中的每一組子陣列的長度都相同,用剛剛的方式壓縮\(\omega\)的運算量!
cd OMEGA(int n, int k);
void SORT(cd *a, int N);
// void CONQUER(pii y0_range, pii y1_range);
void SMALL_CONQUER(cd &L, cd &R, cd omega);
void FFT(cd *a, int N) {
SORT(a, int N);
for (int w = 1; w < N; w <<= 1) {
// w: the width of subarrays that hasn't been conquered
int omega_n = w * 2;
for (int omega_k = 0; omega_k < w; omega_k++) {
cd omega = OMEGA(omega_n, omega_k);
for (int s = 0; s < N; s += w * 2) {
// s: the start position of the first array to be conquered
SMALL_CONQUER(a[s + omega_k], a[s + w + omega_k], omega);
}
}
}
}
typedef long long ll;
typedef pair<int, int> pii;
typedef complex<double> cd;
inline cd cis(double theta) {
return cd(cos(theta), sin(theta));
}
inline cd OMEGA(int n, int k) {
return cis(acos(-1) * 2 * k / n);
}
inline int REVERSE(int x, int N) {
int ans = 0;
for (int i = 1; i < N; i <<= 1) {
ans <<= 1;
if (i & x) ans |= 1;
}
return ans;
}
void FFT(cd *a, int N) {
for (int i = 0; i < N; i++) {
int r = REVERSE(i, N);
if (i < r) swap(a[i], a[r]);
}
/* processing */
for (int w = 1; w < N; w <<= 1) {
int omega_n = w * 2;
for (int omega_k = 0; omega_k < w; omega_k++) {
cd omega = OMEGA(omega_n, omega_k);
for (int s = 0; s < N; s += w << 1) {
cd &L = a[s + omega_k], &R = a[s + omega_k + w];
cd l = L, r = R * omega;
L = l + r;
R = l - r;
}
}
}
}
\(A(x),B(x)\)
\(\alpha,\beta\)
\(c\)
\((A*B)(x)\)
\(\operatorname{DFT}\)
兩兩相乘
\(\operatorname{DFT}^{-1}\)
typedef long long ll;
typedef pair<int, int> pii;
typedef complex<double> cd;
inline cd cis(double theta) {
return cd(cos(theta), sin(theta));
}
inline cd OMEGA(int n, int k) {
return cis(acos(-1) * 2 * k / n);
}
inline int REVERSE(int x, int N) {
int ans = 0;
for (int i = 1; i < N; i <<= 1) {
ans <<= 1;
if (i & x) ans |= 1;
}
return ans;
}
void FFT(cd *a, int N, bool inv) {
for (int i = 0; i < N; i++) {
int r = REVERSE(i, N);
if (i < r) swap(a[i], a[r]);
}
/* processing */
for (int w = 1; w < N; w <<= 1) {
int omega_n = w * 2;
for (int omega_k = 0; omega_k < w; omega_k++) {
cd omega = OMEGA(omega_n, (inv ? -1 : 1) * omega_k);
for (int s = 0; s < N; s += w << 1) {
cd &L = a[s + omega_k], &R = a[s + omega_k + w];
cd l = L, r = R * omega;
L = l + r;
R = l - r;
}
}
}
if (inv) for (int i = 0; i < N; i++) a[i] /= N;
}
(若\(0\leq k<n\))
誠徵:一個定義\(\omega_N^k\)的方式使得
#define int long long
struct Rest {
int val;
const static int mod;
// mod must be a prime
Rest() {
val = 0;
}
Rest(int _x) {
val = _x % mod;
}
Rest operator*(Rest other) {
return Rest(val * other.val % mod);
}
Rest pow(int x) {
Rest now(1);
for (int i = 1 << 30; i > 0; i >>= 1) {
now = now * now;
if (i & x) now = operator*(now);
}
return now;
}
Rest inv() {
return pow(mod - 2);
}
}
令\(\omega_6^k=3^k\%7\)
k | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
omega | 1 | 3 | 2 | 6 | 4 | 5 |
令\(\omega_6^k=3^k\%7\)
k | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
omega | 1 | 3 | 2 | 6 | 4 | 5 |
我們要的是\(N=2^x\)
令\(\omega_{8388608}^k=15311432^k\%998244353\)
我們要的是\(N=2^x\)
令\(\omega_{2^{23}}^k=(3^{119})^k\%998244353\)
我們要的是\(N=2^x\)
令\(\omega_{2^{23}}^k=(3^{119})^k\%998244353\)
我們要的是\(N=2^x\)
它是好的
令\(\omega_{2^{23}}^k=(3^{119})^k\%998244353\)
喔我現在只要用\(\omega_4^k\)
它是好的
令\(\omega_{2^{23}}^k=(3^{119})^k\%998244353\)
喔我現在只要用\(\omega_4^k\)
\(\omega_4^k=\omega_{4\cdot2^{21}}^{k\cdot2^{21}}\)
令\(\omega_{2^{23}}^k=(3^{119})^k\%998244353\)
當\(N\leq2^{23}\),且運算後的數字不超過\(998244353\)時可以使用這套系統
#define int long long
struct Rest {
int val, mod;
Rest() {
val = 0;
mod = 0;
}
Rest(int _val, int _mod) {
val = _val % _mod;
mod = _mod;
}
Rest operator+(Rest other) {
return Rest((val + other.val) % mod, mod);
}
Rest operator-(Rest other) {
return Rest((val - other.val + mod) % mod, mod);
}
Rest operator*(Rest other) {
return Rest(val * other.val % mod, mod);
}
Rest pow(int x) {
Rest ans(1, mod);
for (int i = 1 << 30; i > 0; i >>= 1) {
ans = ans * ans;
if (i & x) ans = operator*(ans);
}
return ans;
}
Rest inv() {
return pow(mod - 2);
}
};
int REVERSE(int id, int N) {
int ans = 0;
for (int i = 1; i < N; i <<= 1) {
ans <<= 1;
if (i & id) ans |= 1;
}
return ans;
}
struct NTTBASE {
Rest g, omegaB, omegaB_1;
int mul, upLG;
NTTBASE() {
g = Rest();
mul = 0;
omegaB = Rest();
omegaB_1 = Rest();
}
NTTBASE(int _g, int _mul, int _upLG, int _mod) {
g = Rest(_g, _mod);
mul = _mul;
upLG = _upLG;
omegaB = g.pow(mul);
omegaB_1 = omegaB.inv();
}
Rest OMEGA(int _lgn, int _k) {
Rest ans = omegaB;
for (int i = _lgn; i < upLG; i++) {
ans = ans * ans;
}
return ans.pow(_k);
}
void operator()(Rest *a, int N, bool inv) {
for (int i = 0; i < N; i++) {
int rev = REVERSE(i, N);
if (i < rev) swap(a[i], a[rev]);
}
for (int w = 1, wlg = 0; w < N; w <<= 1, wlg++) {
Rest omegaBase = OMEGA(wlg + 1, 1), omega(1, g.mod);
if (inv) omegaBase = omegaBase.inv();
for (int omega_k = 0; omega_k < w; omega_k++) {
for (int s = 0; s < N; s += w * 2) {
Rest &L = a[s + omega_k], &R = a[s + w + omega_k];
Rest l = L, r = R * omega;
L = l + r;
R = l - r;
}
omega = omega * omegaBase;
}
}
if (inv) {
Rest temp = Rest(N, g.mod).inv();
for (int i = 0; i < N; i++) a[i] = a[i] * temp;
}
}
};
NTTBASE NTT(3, 119, 23, MOD);
\(\bold{A}*\bold{B}\)會有負次項,怎麼辦?
\(\Rightarrow\)算\(\bold{A}*(x^n\bold{B})\),再除回來就好了