在这些函数中,可以发现它们在 y 这一维度上都和多项式一样。这可能很违反直觉,也可能难以理解,这是因为它和我们常见的操作差异巨大,且比较难找到合适的组合意义。
这类操作都可以封装在一个模板中:
/**
* calculate f(x) = oper(g(x)), where x^S * x^T = { 0 if S ∩ T != ∅
* { x^{S ∪ T} else
* please offer f, g in ll[log len][len], where [x^S]g(x) is stored in g[|S|][S].
*
* @param oper The operation on sets.
* Its type should be void (*) (ll *f, ll *g, int loglen) where f is the destination
*/
template <typename ArrayType, typename Oper> // 理论上 ArrayType 应当是类似 (ll **) 或 (ll *[])
void subset_oper(__restrict__ ArrayType f, __restrict__ ArrayType g, int loglen, const Oper oper)
{
int len = 1 << loglen;
for (int i = 0; i <= loglen; i++)
{
fwt_or(g[i], len, true);
}
static ll F[MAXL], G[MAXL];
for (int j = 0; j < len; j++)
{
for (int i = 0; i <= loglen; i++)
{
G[i] = g[i][j];
}
oper(F, G, loglen);
for (int i = 0; i <= loglen; i++)
{
f[i][j] = F[i];
}
}
ll inl = pow(len, MOD - 2, MOD);
for (int i = 0; i <= loglen; i++)
{
fwt_or(g[i], len, false);
fwt_or(f[i], len, false);
}
}
5.2 \exp 的平方解
事实上,我们已经在求多项式 $\exp$ 时得到了这么一个式子(见多项式全家桶初等函数段):
$$
n[x^n]\exp f(x) = \sum_{i=0}^{n - 1}([x^i]\exp f(x))((n - i)[x^{n - i}]f(x))
$$
这个式子显然是可以 $O(n^2)$ 转移的。
#### 5.3 $\ln$ 操作
首先肯定可以通过 $\int \frac{f'(x)}{f(x)}$ 得到,但过于复杂。考虑到 $\ln$ 是 $\exp$ 的反函数,即:
$$
\begin{aligned}
n[x^n]f(x) &= \sum_{i=0}^{n - 1}([x^i]f(x))((n - i)[x^{n - i}]\ln f(x))\\
n[x^n] \ln f(x) \times \underbrace{[x^0]f(x)}_{=1} &= n[x^n]f(x) - \sum_{i=1}^{n - 1}([x^i]f(x))((n - i)[x^{n - i}]\ln f(x))\\
[x^n]\ln f(x) &= [x^n]f(x) - \frac{\sum_{i=1}^{n - 1}([x^i]f(x))((n - i)[x^{n - i}]\ln f(x))}{n}\\
\end{aligned}
$$
#### 5.4 求逆
这相当于 $\sum f_if^{-1}_{n - i} = [n = 0]$,直接移项就可以得到。我们可以写出以上三者的代码:
```cpp line-numbers
void exp_bf(ll *f, ll *g, int loglen) // 建议不要和我自己写的 POLY::poly_exp 混用,因为 POLY::poly_exp 只支持 2^n 长度
{
const ll *inv = get_inv();
f[0] = 1;
for (int i = 1; i <= loglen; i++)
{
f[i] = 0;
for (int j = 1; j <= i; j++)
{
(f[i] += f[i - j] * g[j] % MOD * j) %= MOD;
}
(f[i] *= inv[i]) %= MOD;
}
}
void ln_bf(ll *f, ll *g, int loglen)
{
const ll *inv = get_inv();
f[0] = 0;
for (int i = 1; i <= loglen; i++)
{
f[i] = 0;
for (int j = 1; j < i; j++)
{
(f[i] += f[j] * g[i - j] % MOD * j) %= MOD;
}
f[i] = (g[i] - inv[i] * f[i] % MOD + MOD) % MOD;
}
}
void inv_bf(ll *f, ll *g, int loglen)
{
f[0] = pow(g[0], MOD - 2, MOD);
for (int i = 1; i <= loglen; i++)
{
f[i] = 0;
for (int j = 1; j <= i; j++)
{
(f[i] += g[j] * f[i - j]) %= MOD;
}
f[i] = (MOD - f[0] * f[i] % MOD) % MOD;
}
}
```
### 6. 完整代码
```cpp line-numbers
#define MAXL 22
#define MAXN 1100005
#define MOD 998244353ll
#define I2 499122177ll
#define popcnt __builtin_popcount
ll pow(ll b, ll p, ll m)
{
ll r = 1;
while (p)
{
if (p & 1)
{
r = r * b % m;
}
b = b * b % m;
p >>= 1;
}
return r;
}
namespace SSPOLY // subset poly
{
void fwt_or(ll *f, int len, bool on)
{
ll type = on ? 1 : -1;
for (int h = 2; h <= len; h <<= 1)
{
for (int j = 0; j < len; j += h)
{
for (int i = 0; i < (h >> 1); i++)
{
(f[j + i + (h >> 1)] += MOD + f[j + i] * type) %= MOD;
}
}
}
}
void fwt_and(ll *f, int len, bool on)
{
ll type = on ? 1 : -1;
for (int h = 2; h <= len; h <<= 1)
{
for (int j = 0; j < len; j += h)
{
for (int i = 0; i < (h >> 1); i++)
{
(f[j + i] += MOD + f[j + i + (h >> 1)] * type) %= MOD;
}
}
}
}
void fwt_xor(ll *f, int len, bool on)
{
ll type = on ? 1 : I2;
for (int h = 2; h <= len; h <<= 1)
{
for (int j = 0; j < len; j += h)
{
for (int i = 0; i < (h >> 1); i++)
{
ll u = f[j + i], t = f[j + i + (h >> 1)];
f[j + i] = (u + t) * type % MOD;
f[j + i + (h >> 1)] = (u - t + MOD) * type % MOD;
}
}
}
}
/**
* @param fwt The FWT function.
* Its type should be void (*) (ll *f, int len, bool on).
* Provided functions are fwt_or, fwt_and, fwt_xor.
*/
template <typename FWT_Type>
void conv(ll __restrict__ *f, ll __restrict__ *g, int len, ll __restrict__ *h, const FWT_Type fwt)
{
fwt(f, len, true);
fwt(g, len, true);
for (int i = 0; i < len; i++)
{
h[i] = f[i] * g[i] % MOD;
}
fwt(f, len, false);
fwt(g, len, false);
fwt(h, len, false);
}
/**
* calculate h(x) = f(x) * g(x), where x^S * x^T = x^{S ∪ T}
*/
void conv_or(ll __restrict__ *f, ll __restrict__ *g, int len, __restrict__ ll *h)
{
conv(f, g, len, h, fwt_or);
}
/**
* calculate h(x) = f(x) * g(x), where x^S * x^T = x^{S ∩ T}
*/
void conv_and(ll __restrict__ *f, ll __restrict__ *g, int len, __restrict__ ll *h)
{
conv(f, g, len, h, fwt_and);
}
/**
* calculate h(x) = f(x) * g(x), where x^S * x^T = x^{S Δ T}
*/
void conv_xor(ll __restrict__ *f, ll __restrict__ *g, int len, __restrict__ ll *h)
{
conv(f, g, len, h, fwt_xor);
}
/**
* calculate h(x) = f(x) * g(x), where x^S * x^T = { 0 if S ∩ T != ∅
* { x^{S ∪ T} else
* please offer f, g, h in ll[log len][len], where [x^S]f(x) is stored in f[|S|][S].
*/
template <typename ArrayType>
void subset_conv(__restrict__ ArrayType f, __restrict__ ArrayType g, int loglen, __restrict__ ArrayType h)
{
int len = 1 << loglen;
for (int i = 0; i <= loglen; i++)
{
fwt_or(f[i], len, true);
fwt_or(g[i], len, true);
}
for (int i = 0; i <= loglen; i++)
{
for (int j = 0; j < len; j++)
{
h[i][j] = 0;
for (int k = 0; k <= i; k++)
{
(h[i][j] += f[k][j] * g[i - k][j] % MOD) %= MOD;
}
}
}
for (int i = 0; i <= loglen; i++)
{
fwt_or(f[i], len, false);
fwt_or(g[i], len, false);
fwt_or(h[i], len, false);
}
}
/**
* only inverse element under log len.
*/
const ll *get_inv()
{
static ll inv[MAXL];
if (!inv[1])
{
for (int i = 1; i < MAXL; i++)
{
inv[i] = pow(i, MOD - 2, MOD);
}
}
return inv;
}
void exp_bf(ll *f, ll *g, int loglen)
{
const ll *inv = get_inv();
f[0] = 1;
for (int i = 1; i <= loglen; i++)
{
f[i] = 0;
for (int j = 1; j <= i; j++)
{
(f[i] += f[i - j] * g[j] % MOD * j) %= MOD;
}
(f[i] *= inv[i]) %= MOD;
}
}
void ln_bf(ll *f, ll *g, int loglen)
{
const ll *inv = get_inv();
f[0] = 0;
for (int i = 1; i <= loglen; i++)
{
f[i] = 0;
for (int j = 1; j < i; j++)
{
(f[i] += f[j] * g[i - j] % MOD * j) %= MOD;
}
f[i] = (g[i] - inv[i] * f[i] % MOD + MOD) % MOD;
}
}
void inv_bf(ll *f, ll *g, int loglen)
{
f[0] = pow(g[0], MOD - 2, MOD);
for (int i = 1; i <= loglen; i++)
{
f[i] = 0;
for (int j = 1; j <= i; j++)
{
(f[i] += g[j] * f[i - j]) %= MOD;
}
f[i] = (MOD - f[0] * f[i] % MOD) % MOD;
}
}
/**
* calculate f(x) = oper(g(x)), where x^S * x^T = { 0 if S ∩ T != ∅
* { x^{S ∪ T} else
* please offer f, g in ll[log len][len], where [x^S]g(x) is stored in g[|S|][S].
*
* @param oper The operation on sets.
* Its type should be void (*) (ll *f, ll *g, int loglen) where f is the destination
* Provided functions are exp_bf, ln_bf, inv_bf
*/
template <typename ArrayType, typename Oper>
void subset_oper(__restrict__ ArrayType f, __restrict__ ArrayType g, int loglen, const Oper oper)
{
int len = 1 << loglen;
for (int i = 0; i <= loglen; i++)
{
fwt_or(g[i], len, true);
}
static ll F[MAXL], G[MAXL];
for (int j = 0; j < len; j++)
{
for (int i = 0; i <= loglen; i++)
{
G[i] = g[i][j];
}
oper(F, G, loglen);
for (int i = 0; i <= loglen; i++)
{
f[i][j] = F[i];
}
}
ll inl = pow(len, MOD - 2, MOD);
for (int i = 0; i <= loglen; i++)
{
fwt_or(g[i], len, false);
fwt_or(f[i], len, false);
}
}
/**
* calculate f(x) = exp(g(x))
* see subset_oper for more detail
*/
template <typename ArrayType>
void subset_exp(__restrict__ ArrayType f, __restrict__ ArrayType g, int loglen)
{
subset_oper(f, g, loglen, exp_bf);
}
/**
* calculate f(x) = ln(g(x))
* see subset_oper for more detail
*/
template <typename ArrayType>
void subset_ln(__restrict__ ArrayType f, __restrict__ ArrayType g, int loglen)
{
subset_oper(f, g, loglen, ln_bf);
}
/**
* calculate f(x) = 1 / g(x)
* see subset_oper for more detail
*/
template <typename ArrayType>
void subset_inv(__restrict__ ArrayType f, __restrict__ ArrayType g, int loglen)
{
subset_oper(f, g, loglen, inv_bf);
}
}
```
### 7. 后记
我也不知道我为什么要学这个,也不知道我能不能记住,实战时更想不到。反正 CCF 不会考,但是 ATC 还是会有的。
实际上多项式全家桶和集合幂级数全家桶还没写完。
已知没写的:多项式复合、复合逆、多项式复合集合幂级数、……