【题解】P4389 付公主的背包 / Euler 变换
思路
EGF + Euler 变换.(有仙人搞出来解析组合做法,但是解析组合是什么?)
Euler 变换
处理一类无标号计数问题。
对于这道题而言,无序选取若干物品,使得其体积之和为 \(m\),是无标号计数。
考虑当前物品的体积为 \(v\),体积为 \(v\) 的物品一共有 \(F[v]\) 个。
考虑对每类物品建立 OGF 然后乘起来,一类物品的 OGF 是 \((1 + x^v + x^2v + \cdots)^{F[v]} = [1 + (x^v)^1 + (x^v)^2 + \cdots]^{F[v]} = (\frac{1}{1 - x^v})^{F[v]}\).
所以答案是 \(\prod\limits_{i = 0} (\frac{1}{1 - x^i})^{F[i]}\).
Euler 变换:\(\operatorname{Euler}(F(x)) = \prod\limits_{i = 0} (\frac{1}{1 - x^i})^{F[i]}\).
考虑处理上面的式子。
考虑将求积转化成求和,因为有 \(\exp \ln F(x) = F(x)\),所以可以考虑先对每一项作 \(\ln\) 转化成求和,最后再 \(\exp\) 回来。
于是原式等于 \(\exp \sum\limits_{i = 0} \ln (\frac{1}{1 - x^i})^{F[i]}\).
考虑到 \(\ln(x^c) = c \ln x\),所以原式等于 \(\exp \sum\limits_{i = 0} F[i] \ln \frac{1}{1 - x^i}\).
令 \(f(x) = \frac{1}{1 - x^v}, g(x) = \ln(f(x))\),考虑化简一下。
这里有一个 似乎人尽皆知的 结论:\(g^{\prime}(x) = \frac{f^{\prime}(x)}{f(x)}\).
其实也算不得什么结论,复合函数导一下就行了。
于是 \(g(x) = \frac{f^{\prime}(x)}{f(x)} = \frac{f^{\prime}(x)}{\frac{1}{1 - x^v}} = (1 - x^v) f^{\prime}(x) = (1 - x^v) \sum\limits_{i = 1} vi \cdot x^{vi - 1}\).
拆开括号发现 \(x^v \sum\limits_{i = 1} vi \cdot x^{vi - 1}\) 实际上是简单移位一下,合并一下系数得 \(g(x) = \sum\limits_{i = 1} v \cdot [i - (i - 1)] \cdot x^{vi - 1} = \sum\limits_{i = 1} v \cdot x^{vi - 1}\).
最后积分回来得 \(g(x) = \sum\limits_{i = 1} \frac{1}{i} x^{vi}\).
所以原式等于 \(\exp \sum\limits_{i = 0} F[i] g(x)\)(\(g(x)\) 中的 \(v\) 为上式的 \(i\)).
暴力做的时间复杂度是调和级数,总复杂度是 \(O(n \log n)\).
对于这个题,令 \(F(x) = \sum\limits_{i = 0} x^i \sum\limits_{j = 1}^n [v_j = i]\),直接求 \(\operatorname{Euler}(F(x))\) 就行。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int sz = 5e5 + 5;
const int mod = 998244353;
const int g = 3;
int n, m;
int cnt[sz], rev[sz], inv[sz];
ll F[sz], G[sz], wp[sz];
ll Ft[sz], Rt[sz], ft[sz], rt[sz], Fn[sz], Rn[sz];
void calc_rev(int k) { for (int i = 1; i < k; i++) rev[i] = (rev[i >> 1] >> 1 | (i & 1 ? k >> 1 : 0)); }
ll qpow(ll base, ll power, ll mod)
{
ll res = 1;
while (power)
{
if (power & 1) res = res * base % mod;
base = base * base % mod;
power >>= 1;
}
return res;
}
void NTT(ll *A, int n)
{
calc_rev(n);
for (int i = 1; i < n; i++)
if (rev[i] > i) swap(A[i], A[rev[i]]);
for (int len = 2, m = 1; len <= n; m = len, len <<= 1)
{
ll wn = qpow(g, (mod - 1) / len, mod);
wp[0] = 1;
for (int i = 1; i <= len; i++) wp[i] = wp[i - 1] * wn % mod;
for (int l = 0, r = len - 1; r <= n; l += len, r += len)
{
int w = 0;
for (int p = l; p < l + m; p++, w++)
{
ll x = A[p], y = wp[w] * A[p + m] % mod;
A[p] = (x + y) % mod, A[p + m] = (x - y + mod) % mod;
}
}
}
}
void INTT(ll *A, int n)
{
NTT(A, n);
reverse(A + 1, A + n);
int inv = qpow(n, mod - 2, mod);
for (int i = 0; i < n; i++) A[i] = 1ll * A[i] * inv % mod;
}
void invp(ll *f, ll *r, int n)
{
int k = 1;
while (k < n) k <<= 1;
r[0] = qpow(f[0], mod - 2, mod);
for (int len = 2, m = 1; len <= k; m = len, len <<= 1)
{
for (int i = 0; i < len; i++) Rt[i] = r[i], Ft[i] = f[i];
NTT(Ft, len), NTT(Rt, len);
for (int i = 0; i < len; i++) Rt[i] = Rt[i] * Ft[i] % mod;
INTT(Rt, len);
for (int i = 0; i < m; i++) Rt[i] = 0; Rt[0] = 1;
for (int i = 0; i < len; i++) Ft[i] = r[i];
NTT(Ft, len), NTT(Rt, len);
for (int i = 0; i < len; i++) Rt[i] = Rt[i] * Ft[i] % mod;
INTT(Rt, len);
for (int i = m; i < len; i++) r[i] = (r[i] * 2ll - Rt[i] + mod) % mod;
}
memset(Ft, 0, k * sizeof(ll));
memset(Rt, 0, k * sizeof(ll));
for (int i = n; i < k; i++) r[i] = 0;
}
void diffp(ll *f, ll *der, int n)
{
for (int i = 1; i < n; i++) der[i - 1] = f[i] * i % mod;
der[n - 1] = 0;
}
void intep(ll *f, ll *inte, int n)
{
for (int i = 1; i < n; i++)
if (!inv[i]) inv[i] = (i == 1 ? 1 : inv[mod % i] * (mod - mod / i) % mod);
for (int i = 1; i < n; i++) inte[i] = f[i - 1] * inv[i] % mod;
inte[0] = 0;
}
void lnp(ll *f, ll *ln, int n)
{
diffp(f, ft, n), invp(f, rt, n);
int k = 1;
while (k < (n << 1)) k <<= 1;
NTT(ft, k), NTT(rt, k);
for (int i = 0; i < k; i++) ft[i] = ft[i] * rt[i] % mod;
INTT(ft, k);
intep(ft, ln, n);
for (int i = 0; i < k; i++) ft[i] = rt[i] = 0;
}
void expp(ll *f, ll *exp, int n)
{
int k = 1;
while (k < n) k <<= 1;
exp[0] = 1;
for (int len = 2, m = 1; len <= k; m = len, len <<= 1)
{
for (int i = 0; i < len; i++) Fn[i] = exp[i];
lnp(Fn, Rn, len);
for (int i = 0; i < len; i++) Rn[i] = (f[i] - Rn[i] + mod) % mod;
Rn[0] = (Rn[0] + 1) % mod;
NTT(Fn, len << 1), NTT(Rn, len << 1);
for (int i = 0; i < (len << 1); i++) Fn[i] = Fn[i] * Rn[i] % mod;
INTT(Fn, len << 1);
for (int i = 0; i < len; i++) exp[i] = Fn[i];
}
for (int i = 0; i < (k << 1); i++) Fn[i] = Rn[i] = 0;
for (int i = n; i < k; i++) exp[i] = 0;
}
int main()
{
scanf("%d%d", &n, &m), m++;
inv[0] = inv[1] = 1;
for (int i = 2; i < m; i++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
for (int i = 1, v; i <= n; i++) scanf("%d", &v), cnt[v]++;
for (int i = 0; i < m; i++)
if (cnt[i])
for (int j = i; j < m; j += i)
F[j] = (F[j] + 1ll * cnt[i] * inv[j / i] % mod) % mod;
expp(F, G, m);
for (int i = 1; i < m; i++) printf("%d\n", G[i]);
return 0;
}