多项式学习笔记…
多项式求逆
给定一个多项式 g(x) ,求一个多项式 f(x) 满足:
f(x)∗g(x)≡1(modxn)
也记作 f(x)≡g−1(x)(modxn)。
令 f∗(x)≡g−1(x)(modxn/2):
f∗(x)f∗(x)−f(x)(f∗(x)−f(x))2f∗2(x)−2f∗(x)f(x)+f2(x)f∗2(x)g(x)−2f∗(x)+f(x)≡f(x)≡0≡0≡0≡0(modxn/2)(modxn/2)(modxn)(modxn)(modxn)
f(x)≡f∗(x)(2−f∗(x)g(x))(modxn)
倍增即可。
时间复杂度 O(nlogn)。
多项式开方
给定一个多项式 g(x) ,求一个多项式 f(x) 满足:
f2(x)≡g(x)(modxn)
令 f∗2(x)≡g(x)(modxn/2):
f∗2(x)f∗2(x)−g(x)(f∗2(x)−g(x))2(f∗2(x)+g(x))2(2f∗(x)f∗2(x)+g(x))22f∗(x)f∗2(x)+g(x)≡g(x)≡0≡0≡4f∗2(x)g(x)≡g(x)≡f(x)(modxn/2)(modxn/2)(modxn)(modxn)(modxn)(modxn)
f(x)≡2−1f∗(x)+2−1g(x)f∗−1(x)(modxn)
倍增即可。
时间复杂度 O(nlogn)。
多项式带余除法
给定一个 n 次多项式 f(x),一个次数为 m 的多项式 g(x) ,求出多项式 Q(x),R(x) 满足:
degQ=n−m,degR<m
f(x)=Q(x)∗g(x)+R(x)
即 Q(x) 为商,R(x) 为余数。
定义一种变换(这里的 R 更 R(x) 没关系):
fR(x)=f(x1)xdegf
其实就是把系数 reverse
一下。
考虑把几个多项式都搞成变换过的形式:
f(x)f(x1)xnf(x1)fR(x)=Q(x)∗g(x)+R(x)=Q(x1)∗g(x1)+R(x1)=xn−mQ(x1)∗xmg(x1)+xn−m+1xm−1(x1)=QR(x)∗gR(x)+xn−m+1RR(x)
放在模 xn−m+1 在 R(x) 就没了,然后 degQ=n−m<n−m+1 不会受到影响。
fR(x)=QR(x)∗gR(x)(modxn−m+1)
多项式求逆可求 Q(x) ,再求 R(x)=f(x)−Q(x)∗g(x) 即可。
多项式求导/积分
对于多项式 f(x) 。
求导:
f′(x)=i=0∑[xi]f(x)⋅ixi−1
[xn]g(x)=(n+1)[xn+1]f(x)
积分:
∫f(x)dx=C+i=0∑[xi]f(x)⋅i+11xi+1
[xn]g(x)=n1[xn−1]f(x)
复杂度都是 O(n) 的。
多项式牛顿迭代
给定一个多项式 g(x) ,求模 xn 意义下的 f(x) 满足:
g(f(x))≡0(modxn)
结论:
f(x)≡f∗(x)−g′(f∗(x))g(f∗(x))(modxn)
f∗(x) 是已经找到的在模 xn/2 意义下的解,上式是一个倍增。
干啥的,方便推导一些东西。
求逆
f(x)−1≡h(x)(modxn)
设 g(f(x))=f−1(x)−h(x) ,有方程 g(f(x))≡0(modxn) 。牛顿迭代一下:
f(x)f(x)≡f∗(x)−−f∗−2(x)f∗−1(x)−h(x)≡2f∗(x)−f∗2(x)h(x)(modxn)(modxn)
开方
f2(x)≡h(x)(modxn)
设 g(f(x))=f2(x)−h(x) ,有方程 g(f(x))≡0(modxn) 。牛顿迭代一下:
f(x)f(x)≡f∗(x)−2f∗(x)f∗2(x)−h(x)≡2−1f∗(x)+2−1h(x)f∗−1(x)(modxn)(modxn)
EXP
lnf(x)≡h(x)(modxn)
设 g(f(x))=lnf(x)−h(x) ,有方程 g(f(x))≡0(modxn) 。牛顿迭代一下:
f(x)f(x)≡f∗(x)−f∗−1(x)lnf∗(x)−h(x)(modxn)≡f∗(x)(1−lnf∗(x)+h(x))(modxn)
以上推倒的式子复杂度都为:
T(n)=T(2n)+O(nlogn)=O(nlogn)
话说这里推导的求逆和开方和之前的是一样的,但显然用牛顿迭代推导更方便。
多项式对数函数
给定一个多项式 g(x) ,求一个多项式 f(x) 满足:
f(x)≡lng(x)(modxn)
具体的由麦克劳林级数定义:
lnf(x)=−i=1∑+∞i(1−f(x))i
若 lng(x) 存在需满足 [x0]g(x)=1
有 ln′x=x1,两边同时求导,注意链式法则。再积分就完事了。
f(x)f′(x)f(x)≡lng(x)≡g(x)g′(x)≡∫g(x)g′(x)dx(modxn)(modxn)(modxn)
一次求导,一次求逆,一次乘法,一次积分完事了。
复杂度 O(nlogn) 。
递推法:
f(x)f′(x)g(x)≡lng(x)≡g′(x)(modxn)(modxn)
i=0∑n[xi]f′(x)[xn−i]g(x)i=0∑n(i+1)[xi+1]f′(x)[xn−i]g(x)=[xn]g′(x)=n[xn+1]g′(x)
[xn]f(x)=[xn]g(x)−n1i=1∑n−1i[xn−i]g(x)[xi]f(x)
多项式指数函数
给定一个多项式 g(x) ,求一个多项式 f(x) 满足:
f(x)≡expg(x)(modxn)
具体的由麦克劳林级数定义:
expf(x)=i=0∑+∞i!fi(x)
若 expg(x) (即 eg(x))存在需满足 [x0]g(x)=0。
注:exp′x=expx
通过牛顿迭代给出了一种 O(nlogn) 的倍增做法: EXP 。再来考虑一下普通做法:
f(x)f′(x)≡expg(x)≡f(x)g′(x)(modxn)(modxn)
提取系数:
[xn]f′(x)(n+1)[xn+1]f(x)[xn]f(x)=i=0∑n[xn−i]f(x)⋅[xi]g′(x)=i=0∑n[xn−i]f(x)⋅(i+1)[xi+1]g(x)=n1i=1∑n[xn−i]f(x)⋅i[xi]g(x)
注:i=1 枚举因为 [x0]g(x)=0
分治 FFT ,复杂度 O(nlog2n) ,实际上常数小,比倍增的快。
多项式快速幂
给定一个多项式 g(x) ,求一个多项式 f(x) 满足:
f(x)≡gk(x)(modxn)
如果只是 niave 多项式乘法+快速幂,复杂度 O(nlognlogk)。
显然有更好的做法:
f(x)=gk(x)=e(lng(x))k
会 EXP,Ln 便完事了,复杂度 O(nlogn) ,但需要满足 [x0]g(x)=1 。
注意复杂度与 k 无关,k 只要 mod998244353 即可。(k 跟 lng(x) 是数乘)
代码实现
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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define clr(f,n) memset(f,0,sizeof(long long)*(n)) #define cpy(f,g,n) memcpy(f,g,sizeof(long long)*(n)) #define Outarr(x,n) cerr<<#x<<" : "; for(int i=0;i<n;++i) cerr<<x[i]<<" ";cout<<endl; #define outarr(x,n) for(int i=0;i<n;++i) printf("%lld%c",x[i],(i==n-1)?'\n':' '); #define MOD(x) ((x)<mod?(x):((x)%mod)) using namespace std; template<typename _Tp> inline void red(_Tp &x) { x = 0; bool fg = 0; char ch = getchar(); while (ch < '0' || ch > '9') {if (ch == '-') fg ^= 1; ch = getchar();} while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (_Tp)(ch ^ 48), ch = getchar(); if (fg) x = -x; } typedef long long ll;
namespace poly { const int mod = 998244353; const int N = (1 << 19); const int _G = 3; const int _iG = 332748118; const int inv2 = 499122177;
ll fpow(ll a, ll b, ll p) { ll r = 1; for (; b; a = a * a % p, b >>= 1) if (b & 1) r = r * a % p; return r; }
int rev[N], rev_n; void prerev(int n) { if (n == rev_n) return; rev_n = n; for (int i = 0; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (n >> 1) : 0); } void NTT(ll f[], int n, int fg) { prerev(n); for (int i = 0; i < n; ++i) if (i < rev[i]) swap(f[i], f[rev[i]]); for (int h = 2; h <= n; h <<= 1) { ll Dt = fpow((fg == 1) ? _G : _iG, (mod - 1) / h, mod), w; int len = h >> 1; for (int j = 0; j < n; j += h) { w = 1; for (int k = j; k < j + len; ++k) { ll tmp = MOD(f[k + len] * w); f[k + len] = f[k] - tmp; (f[k + len] < 0) &&(f[k + len] += mod); f[k] = f[k] + tmp; (f[k] >= mod) &&(f[k] -= mod); w = MOD(w * Dt); } } } if (fg == -1) { ll invn = fpow(n, mod - 2, mod); for (int i = 0; i < n; ++i) f[i] = MOD(f[i] * invn); } } void p_mul(ll f[], ll g[], int n, int m, int len) { static ll a[N], b[N]; int nn = 1 << (int)ceil(log2(n + m - 1)); clr(a, nn); clr(b, nn); cpy(a, f, n); cpy(b, g, m); NTT(a, nn, 1); NTT(b, nn, 1); for (int i = 0; i < nn; ++i) a[i] = MOD(a[i] * b[i]); NTT(a, nn, -1); for (int i = 0; i < len; ++i) f[i] = a[i]; } void p_inv(ll g[], int n, ll f[]) { static ll sav[N]; int nn = 1 << (int)ceil(log2(n)); clr(f, n * 2); f[0] = fpow(g[0], mod - 2, mod); for (int h = 2; h <= nn; h <<= 1) { cpy(sav, g, h); clr(sav + h, h); NTT(sav, h << 1, 1); NTT(f, h << 1, 1); for (int i = 0; i < (h << 1); ++i) { f[i] = f[i] * (2ll - f[i] * sav[i] % mod + mod) % mod; } NTT(f, h << 1, -1); clr(f + h, h); } clr(f + n, nn * 2 - n); } void p_sqrt(ll g[], int n, ll f[]) { static ll sav[N], r[N]; int nn = 1 << (int)ceil(log2(n)); clr(f, n * 2); f[0] = 1; for (int h = 2; h <= nn; h <<= 1) { cpy(sav, g, h); clr(sav + h, h); p_inv(f, h, r); NTT(sav, h << 1, 1); NTT(r, h << 1, 1); for (int i = 0; i < (h << 1); ++i) sav[i] = MOD(sav[i] * r[i]); NTT(sav, h << 1, -1); for (int i = 0; i < h; ++i) f[i] = MOD((f[i] + sav[i]) * inv2); clr(f + h, h); } clr(f + n, nn * 2 - n); } void p_div(ll f[], ll g[], int n, int m, ll q[], ll r[]) { static ll sav1[N], sav2[N]; int nn; for (nn = 1; nn < n - m + 1; nn <<= 1); clr(sav1, nn); clr(sav2, nn); cpy(sav1, f, n); cpy(sav2, g, m); reverse(sav1, sav1 + n); reverse(sav2, sav2 + m); p_inv(sav2, n - m + 1, q); p_mul(q, sav1, n - m + 1, n, n - m + 1); reverse(q, q + n - m + 1); cpy(r, g, m); p_mul(r, q, m, n - m + 1, m - 1); for (int i = 0; i < m - 1; ++i) r[i] = MOD(f[i] - r[i] + mod); } ll inv[N]; void Initinv(int n) { inv[0] = inv[1] = 1; for (int i = 2; i <= n; ++i) inv[i] = (mod - mod / i) * inv[mod % i] % mod; } void p_int(ll f[], int n) { for (int i = n - 1; i; --i) f[i] = MOD(f[i - 1] * inv[i]); f[0] = 0; } void p_der(ll f[], int n) { for (int i = 1; i < n; ++i) f[i - 1] = MOD(f[i] * i); f[n - 1] = 0; } void p_ln(ll f[], int n) { static ll g[N]; p_inv(f, n, g); p_der(f, n); p_mul(f, g, n, n, n + n); p_int(f, n); }
void p_exp(ll f[], int n) { static ll g[N], sav[N]; clr(g, n * 2); clr(sav, n * 2); g[0] = 1; for (int h = 2; h <= n; h <<= 1) { cpy(sav, g, h); p_ln(sav, h); for (int i = 0; i < h; ++i) sav[i] = MOD(f[i] - sav[i] + mod); sav[0] = MOD(sav[0] + 1); p_mul(g, sav, h, h, h); } cpy(f, g, n); }
void p_pow(ll f[], int n, ll k) { p_ln(f, n); for (int i = 0; i < n; ++i) f[i] = MOD(f[i] * k); p_exp(f, n); }
void DCFFT(ll f[], ll g[], int l, int r) { static ll A[N], B[N]; if (r - l == 1) return ; int mid = (l + r) >> 1, len = mid - l; DCFFT(f, g, l, mid); cpy(A, f + l, len); clr(A + len, len); cpy(B, g, len << 1); p_mul(A, B, len << 1, len << 1, len << 1); for (int i = mid; i < r; ++i) f[i] = MOD(f[i] + A[i - l]); DCFFT(f, g, mid, r); } }
|