Loading

【题解】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;
}
posted @ 2023-02-19 16:39  kymru  阅读(29)  评论(0编辑  收藏  举报