一、引入
有时候,我们会遇到这种形式的问题:
给定一个定义域为
且自变量为整数的函数(数组)
,有一个未知函数
,
给定
与
的卷积
,求
。
这时候要用到一个技巧——多项式求逆。
二、概念
一个
项
次多项式
的逆元,定义为一个多项式
,满足:
显然 。如果暴力求是 的。用 FFT 可以做到 的复杂度。
三、过程
基本思想是分治。假设已经求得了模
的逆元
,那么有
即
两边同时平方:
两边同乘 :
所以得到 的递推式:
边界:
从 一直递推到 ( )就能求出 。
四、示例
回到引入中的问题。容易发现,我们要求的就是
。
BZOJ 3456 城市规划(无向连通图计数)
我们知道,无向连通图计数的递推公式为:
边界 。
很容易将其转化为含有 , , 的式子:
令 , , ,
那么有:
即:
因此:
用多项式求逆即可得解。
五、代码
下面给一个 NTT 的多项式求逆:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Pow(k, n) for (k = 1; k < n; k <<= 1)
#define Step(i, a, b, x) for (i = a; i <= b; i += x)
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 1e6 + 5, PYZ = 998244353;
int n, f[N], g[N], m = 2, ff, cnt, tmp[N], cm[N], rev[N], sp[N];
int qpow(int a, int b) {
int res = 1; while (b) b & 1 ? res = 1ll * res * a % PYZ : 0,
a = 1ll * a * a % PYZ, b >>= 1; return res;
}
void FFT(int n, int *a, int op) {
int i, j, k, x; For (i, 0, n - 1) if (i < rev[i]) swap(a[i], a[rev[i]]);
x = qpow(op == 1 ? 3 : 332748118, (PYZ - 1) / n); k = n >> 1;
while (1) {sp[k] = x; x = 1ll * x * x % PYZ; if (!k) break; k >>= 1;}
Pow (k, n) {
x = sp[k]; Step (i, 0, n - 1, k << 1) {
int w = 1; For (j, 0, k - 1) {
int u = a[i + j], v = 1ll * w * a[i + j + k] % PYZ;
a[i + j] = (u + v) % PYZ; a[i + j + k] = (u - v + PYZ) % PYZ;
w = 1ll * w * x % PYZ;
}
}
}
}
int main() {
int i, rp; n = read(); For (i, 0, n - 1) f[i] = read();
g[0] = qpow(f[0], PYZ - 2); while (1) {
ff = 1; cnt = 0; while (ff < (m << 1)) ff <<= 1, cnt++;
For (i, 0, (m >> 1) - 1) tmp[i] = g[i]; For (i, 0, m - 1) cm[i] = f[i];
For (i, m >> 1, ff - 1) tmp[i] = 0; For (i, m, ff - 1) cm[i] = 0;
rp = qpow(ff, PYZ - 2); rev[0] = 0; For (i, 1, ff - 1)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << cnt - 1);
FFT(ff, tmp, 1); For (i, 0, ff - 1) tmp[i] = 1ll * tmp[i] * tmp[i] % PYZ;
FFT(ff, cm, 1); For (i, 0, ff - 1) tmp[i] = 1ll * tmp[i] * cm[i] % PYZ;
FFT(ff, tmp, -1); For (i, 0, ff - 1) tmp[i] = 1ll * tmp[i] * rp % PYZ;
For (i, 0, (m >> 1) - 1) g[i] = 2ll * g[i] % PYZ;
For (i, 0, m - 1) g[i] = (g[i] - tmp[i] + PYZ) % PYZ;
if (m >= n) break; m <<= 1;
}
For (i, 0, n - 1) printf("%d ", g[i]); return 0;
}