多项式开方
描述¶
给定多项式
解法¶
倍增法¶
假设现在已经求出了
倍增计算即可。
时间复杂度
还有一种常数较小的写法就是在倍增维护
当
时,可能需要使用二次剩余来计算 \left[x^{0}\right]g\left(x\right)\neq 1 。 \left[x^{0}\right]f\left(x\right)
洛谷模板题 P5205 【模板】多项式开根 参考代码
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | #include <bits/stdc++.h>
using namespace std;
const int maxn = 1 << 20, mod = 998244353;
int a[maxn], b[maxn], g[maxn], gg[maxn];
int qpow(int x, int y) { //快速幂
int ans = 1;
while (y) {
if (y & 1) {
ans = (long long)1 * ans * x % mod;
}
x = (long long)1 * x * x % mod;
y >>= 1;
}
return ans;
}
int inv2 = qpow(2, mod - 2); //逆元
inline void change(int *f, int len) {
for (int i = 1, j = len / 2; i < len - 1; i++) {
if (i < j) {
swap(f[i], f[j]);
}
int k = len / 2;
while (j >= k) {
j -= k;
k /= 2;
}
if (j < k) {
j += k;
}
}
}
inline void NTT(int *f, int len, int type) { // NTT
change(f, len);
for (int q = 2; q <= len; q <<= 1) {
int nxt = qpow(3, (mod - 1) / q);
for (int i = 0; i < len; i += q) {
int w = 1;
for (int k = i; k < i + (q >> 1); k++) {
int x = f[k];
int y = (long long)1 * w * f[k + (q / 2)] % mod;
f[k] = (x + y) % mod;
f[k + (q / 2)] = (x - y + mod) % mod;
w = (long long)1 * w * nxt % mod;
}
}
}
if (type == -1) {
reverse(f + 1, f + len);
int iv = qpow(len, mod - 2);
for (int i = 0; i < len; i++) {
f[i] = (long long)1 * f[i] * iv % mod;
}
}
}
inline void inv(int deg, int *f, int *h) { //求逆元
if (deg == 1) {
h[0] = qpow(f[0], mod - 2);
return;
}
inv(deg + 1 >> 1, f, h);
int len = 1;
while (len < deg * 2) { //倍增
len *= 2;
}
copy(f, f + deg, gg);
fill(gg + deg, gg + len, 0);
NTT(gg, len, 1);
NTT(h, len, 1);
for (int i = 0; i < len; i++) {
h[i] = (long long)1 * (2 - (long long)1 * gg[i] * h[i] % mod + mod) % mod *
h[i] % mod;
}
NTT(h, len, -1);
fill(h + deg, h + len, 0);
}
int n, t[maxn];
// deg:次数
// f:被开根数组
// h:答案数组
inline void sqrt(int deg, int *f, int *h) {
if (deg == 1) {
h[0] = 1;
return;
}
sqrt(deg + 1 >> 1, f, h);
int len = 1;
while (len < deg * 2) { //倍增
len *= 2;
}
fill(g, g + len, 0);
inv(deg, h, g);
copy(f, f + deg, t);
fill(t + deg, t + len, 0);
NTT(t, len, 1);
NTT(g, len, 1);
NTT(h, len, 1);
for (int i = 0; i < len; i++) {
h[i] = (long long)1 * inv2 *
((long long)1 * h[i] % mod + (long long)1 * g[i] * t[i] % mod) % mod;
}
NTT(h, len, -1);
fill(h + deg, h + len, 0);
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
sqrt(n, a, b);
for (int i = 0; i < n; i++) {
printf("%d ", b[i]);
}
return 0;
}
|
Newton's Method¶
参见 Newton's Method.
例题¶
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用