10 进制 FWT 的写法。
题意:给定 $n$ 个整数 $a_1, a_2, \dots, a_n$,求有多少个长度为 $n$ 的序列满足每一个数都从 $n$ 个数里面选(不同位置算不同方案),且 10 进制不进位加法后答案为 $x$。输出 $x\in [0, n - 1]$ 的答案。$n\leq 10 ^ 5, a_i < 10 ^ 5$,答案对 $2 ^ {58}$ 取模。
容易发现我们只需要实现十进制 FWT,直接快速幂再逆变换即可。
首先考虑 FWT 的本质其实是一种高维的 FFT,只不过每一维的长度都是 2。现在考虑如何变为长度为 10。
FFT 的过程需要乘上 $\omega_{10} ^ k$,这个在 $\bmod 2 ^ {58}$ 下似乎是没有整数与之对应,所以我们需要用一个多项式表示,为 $\sum_{i = 0} ^ 9 a_i \omega_{10}^i$。用这个就可以使用 FFT 计算了。
另外,我们还需要考虑另外的问题:最后我们每一维都需要除以一个 10,最后就是除以 $10 ^ 5$,但是 $10 ^ 5$ 在 $\bmod 2 ^ {58}$ 意义下没有逆元。容易发现我们是因为有 $2 ^ 5$ 的缘故,我们可以保留答案 $\bmod 2 ^ {63}$ 意义下的结果,最后直接除,就不需要管逆元了。$5 ^ 5$ 在 $\bmod 2 ^ {63}$ 意义下的逆元可以扩欧计算,所以就可以算了。
最后我们得到的答案是一个多项式,但是由于单位根的性质,表示并不是唯一的。$\omega_{10} ^ 5 = -1, \omega_{10} ^ 9 = -\sum_{i = 0} ^ 8 \omega_{10} ^ i$,用这两个式子我们可以把这个多项式化成 $\sum_{i = 0} ^ 3 a_i\omega_{10}^i$,不能再转换,可以证明盲猜一下这时 $a_0$ 的答案就是最终答案。
实际代码中,我们使用 unsigned long long
计算即可。时间复杂度不好算,不过 unsigned long long
常数小,可以通过。
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
| const ULL inv5 = 14757395258967641293ULL; struct Complex { ULL a[5]; ULL& operator [](int x) { return a[x]; } Complex operator +(Complex t) { return {a[0] + t[0], a[1] + t[1], a[2] + t[2], a[3] + t[3], a[4] + t[4]}; } Complex operator -(Complex t) { return {a[0] - t[0], a[1] - t[1], a[2] - t[2], a[3] - t[3], a[4] - t[4]}; } } f[N]; Complex operator *(Complex a, Complex b) { Complex res{}; for (int i = 0; i < 5; ++ i) for (int j = 0; j < 5; ++ j) if (i + j >= 5) res[i + j - 5] -= a[i] * b[j]; else res[i + j] += a[i] * b[j]; return res; } Complex qpow(Complex a, int k) { Complex res{1, 0, 0, 0, 0}; for (; k; k >>= 1, a = a * a) if (k & 1) res = res * a; return res; } Complex transw(Complex a, int typ) { Complex res{}; for (int i = 0; i < 5; ++ i) { int to = (i + typ + 10) % 10; if (to >= 5) res[to - 5] -= a[i]; else res[to] += a[i]; } return res; } void FWT(Complex a[], int typ) { for (int mid = 1; mid < N; mid *= 10) for (int i = 0; i < N; i += mid * 10) for (int j = 0; j < mid; ++ j) { Complex tmp[10]; for (int k = 0; k < 10; ++ k) tmp[k] = a[i + mid * k + j]; for (int k = 0; k < 10; ++ k) { Complex &cur = a[i + mid * k + j]; cur = {}; for (int p = 0; p < 10; ++ p) cur = cur + tmp[p]; for (int p = 0; p < 10; ++ p) tmp[p] = transw(tmp[p], typ * p); } } if (typ == 1) return; ULL inv = inv5 * inv5 * inv5 * inv5 * inv5; for (int i = 0; i < N; ++ i) for (int j = 0; j < 5; ++ j) a[i][j] *= inv; }
|