Candy Shop - ieeextreme15


大佬1:這題生成函數,比較基礎吧

大佬2:確實基礎

我:才發現只有我是零基礎

題目大意:
https://csacademy.com/ieeextreme15/task/candy-shop/statement/
有N種糖果,每種糖果有{ai包,每包含有bi塊}。問湊齊恰好K塊糖果有多少種方法?

1<=N,K<=10^5;

1<=ai,bi<=10^5;

參考題解 洛谷P4389

(注:本文代碼題解都是抄自洛谷一位大佬,幾乎非原創,咸魚只是為了做個記錄

大佬博客 https://www.cnblogs.com/PinkRabbit/p/10423084.html)

比賽的時候,毫無思路,瞄到60%的數據N,K<=1000。只能dp騙60分

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int N = 1e5 + 5;
int dp[2][N];
int main()
{
    int n, K, nw, pr;
    scanf("%d%d", &n, &K);

    dp[0][0] = 1;
    for (int i = 1, a, b; i <= n; i++)
    {
        scanf("%d%d", &a, &b);
        nw = i & 1;
        pr = !nw;
        for (int j = 0; j <= K; j++)
        {
            dp[nw][j] = dp[pr][j];
            for (int k = 1; k <= a && j - k * b >= 0; k++)
            {
                dp[nw][j] += dp[pr][j - k * b];
                dp[nw][j] %= mod;
            }
        }
    }
    printf("%d\n", int(dp[nw][K] % mod));
    return 0;
}
/*
Verdict: 56.68 points (60%)
Language: C++
CPU Time usage: 3089 ms
Memory usage: 1516 KB

最壞時間復雜度NKK。由於題目數據Ai,Bi可能是隨機分布的,所以平均時間復雜度NKlgK
*/

1.生成函數(組合數學)

假設某糖果有{a=5包,每包b=3塊}那么生成函數為

\[S1=1+x^3+x^6+x^9+x^{12}+x^{15}=(1-x^{3*(5+1)})/(1-x^3) \]

另一種糖果有{a=3包,每包b=2塊}

\[S2=1+x^2+x^4+x^6=(1-x^{2*(3+1)})/(1-x^2) \]

只用這兩種湊齊k塊的方案數,就是表達式S1*S2里頭x^k前面的系數(廢話)

等比數列求個和-->生成函數

\[(1-x^{b*(a+1)})/(1-x^b) \]

設答案的生成函數為F,則有

\[F=\prod_{i=1}^N (1-x^{b_i*({a_i}+1)})/(1-x^{b_i}) \]

2.公式推倒

兩邊取對數:

\[ln F = \sum_{i=1}^N ln(1-x^{b_i*({a_i}+1)})-ln(1-x^{b_i}) \]

\[ln(1-x)=-\sum_{j=1}{x^j}/j \]

\[lnF =\sum_{i=1}^N (\sum_{j=1}{x^{b_i*j}}/j - \sum_{j=1}{x^{b_i*({a_i}+1)*j}}/j) \]

\[F=e^{lnF} \]

至此就完事了,解出x^k的系數

3.公式部分時間復雜度

由上公式,易知每一輪j的上界為K/bi和K/(bi * (ai + 1))

雖然最壞情況下時間復雜度為NK,但由於題目數據Bi可能是隨機分布,所以估算時間復雜度為NlgK,能過掉此題。

為了把復雜度K嚴格地降到lgK,可以預先統計bi和bi(ai+1)的個數cnt,就能用如下的公式得到KlgK的復雜度

\[lnF =\sum (\sum_{j=1}{x^{b*j}}/j*cnt_b) - \sum(\sum_{j=1}{x^{b*({a}+1)*j}}/j*cnt_{b*(a+1)}) \]

AC代碼1(未預處理統計)

//代碼抄自洛谷P4389 付公主的背包
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mod = 998244353;
const int G = 3, iG = 332748118;
const int maxn = 1 << 18;

inline ll fpow(ll b, int e) //快速冪b^e。人菜數論只會gcd
{
    ll a = 1;
    for (; e; e >>= 1, b = b * b % mod)
        if (e & 1)
            a = a * b % mod;
    return a;
}
//下面的函數就真的不知道啥玩意了,畢竟真沒學過數學
ll Inv[maxn];

inline void Init(int N) //這里可能是逆元打表
{
    Inv[0] = Inv[1] = 1;
    for (int i = 2; i < N; ++i)
        Inv[i] = 1ll * Inv[mod % i] * (mod - mod / i) % mod;
}

int Sz, R[maxn];
ll InvSz;

inline void InitFNTT(int N) //初始化快速數論變換?
{
    int Bt = 0;
    for (; 1 << Bt < N; ++Bt)
        ;
    if (Sz == (1 << Bt))
        return;
    Sz = 1 << Bt;
    InvSz = -(mod - 1) / Sz;
    for (int i = 1; i < Sz; ++i)
        R[i] = R[i >> 1] >> 1 | (i & 1) << (Bt - 1);
}

inline void FNTT(ll *A, int Ty) //快速數論變換,確實不會啊
{
    for (int i = 0; i < Sz; ++i)
        if (R[i] < i)
            swap(A[R[i]], A[i]);
    for (int j = 1, j2 = 2; j < Sz; j <<= 1, j2 <<= 1)
    {
        ll gn = fpow(~Ty ? G : iG, (mod - 1) / j2), g, X, Y;
        for (int i = 0, k; i < Sz; i += j2)
        {
            for (k = 0, g = 1; k < j; ++k, g = g * gn % mod)
            {
                X = A[i + k], Y = g * A[i + j + k] % mod;
                A[i + k] = (X + Y) % mod, A[i + j + k] = (X - Y) % mod;
            }
        }
    }
    if (!~Ty)
        for (int i = 0; i < Sz; ++i)
            A[i] = A[i] * InvSz % mod;
}

inline void PolyInv(ll *A, int N, ll *B) //這里可能是多項式求逆
{
    static ll tA[maxn], tB[maxn];
    B[0] = fpow(A[0], mod - 2);
    for (int L = 1; L < N; L <<= 1)
    {
        int L2 = L << 1, L4 = L << 2;
        InitFNTT(L4);
        memcpy(tA, A, 8 * L2);
        memset(tA + L2, 0, 8 * (Sz - L2));
        memcpy(tB, B, 8 * L);
        memset(tB + L, 0, 8 * (Sz - L));
        FNTT(tA, 1), FNTT(tB, 1);
        for (int i = 0; i < Sz; ++i)
            tB[i] = (2 - tB[i] * tA[i]) % mod * tB[i] % mod;
        FNTT(tB, -1);
        for (int i = 0; i < L2; ++i)
            B[i] = tB[i];
    }
}

inline void PolyLn(ll *A, int N, ll *B) //顧名思義ba
{
    static ll tA[maxn], tB[maxn];
    PolyInv(A, N - 1, tB);
    InitFNTT(N * 2 - 3);
    for (int i = 1; i < N; ++i)
        tA[i - 1] = i * A[i] % mod;
    memset(tA + N - 1, 0, 8 * (Sz - N + 1));
    memset(tB + N - 1, 0, 8 * (Sz - N + 1));
    FNTT(tA, 1), FNTT(tB, 1);
    for (int i = 0; i < Sz; ++i)
        tA[i] = tA[i] * tB[i] % mod;
    FNTT(tA, -1);
    B[0] = 0;
    for (int i = 1; i < N; ++i)
        B[i] = tA[i - 1] * Inv[i] % mod;
}

inline void PolyExp(ll *A, int N, ll *B) //多項式exp
{
    static ll tA[maxn], tB[maxn];
    B[0] = 1;
    for (int L = 1; L < N; L <<= 1)
    {
        int L2 = L << 1, L4 = L << 2;
        memset(B + L, 0, 8 * (L2 - L));
        PolyLn(B, L2, tB);
        InitFNTT(L4);
        memcpy(tA, B, 8 * L);
        memset(tA + L, 0, 8 * (Sz - L));
        for (int i = 0; i < L2; ++i)
            tB[i] = ((!i) - tB[i] + A[i]) % mod;
        memset(tB + L2, 0, 8 * (Sz - L2));
        FNTT(tA, 1), FNTT(tB, 1);
        for (int i = 0; i < Sz; ++i)
            tA[i] = tA[i] * tB[i] % mod;
        FNTT(tA, -1);
        for (int i = 0; i < L2; ++i)
            B[i] = tA[i];
    }
}
///各種奇怪又十分基礎的函數終於過了,都快絕望了..ToT
int N, K;
ll A[maxn], B[maxn];
int main()
{
    scanf("%d%d", &N, &K);
    Init(maxn);
    //公式部分==如果輸入是隨機數據那么平均復雜度為nlgk
    for (int i = 1; i <= N; ++i)
    {
        int ai, bi;
        scanf("%d%d", &ai, &bi);
        for (ll j = 1; j <= K / bi; ++j)
        {
            A[bi * j] = (A[bi * j] + Inv[j]) % mod;
        }
        ll val = bi * (ai + 1ll);
        for (ll j = 1; j <= K / val; ++j)
        {
            A[val * j] = (A[val * j] - Inv[j] + mod) % mod;
        }
    }
    //
    PolyExp(A, K + 1, B);
    cout << (B[K] + mod) % mod << endl;
    return 0;
}

// Verdict: 100 points

// Language: C++

// CPU Time usage: 2642 ms

// Memory usage: 17.1 MB

AC代碼2(公式復雜度降到KlgK)

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int mod = 998244353;
const int G = 3, iG = 332748118;
const int MS = 1 << 18;

inline ll qPow(ll b, int e)
{
    ll a = 1;
    for (; e; e >>= 1, b = b * b % mod)
        if (e & 1)
            a = a * b % mod;
    return a;
}

ll Inv[MS];

inline void Init(int N)
{
    Inv[0] = Inv[1] = 1;
    for (int i = 2; i < N; ++i)
        Inv[i] = 1ll * Inv[mod % i] * (mod - mod / i) % mod;
}

int Sz, R[MS];
ll InvSz;

inline void InitFNTT(int N)
{
    int Bt = 0;
    for (; 1 << Bt < N; ++Bt)
        ;
    if (Sz == (1 << Bt))
        return;
    Sz = 1 << Bt;
    InvSz = -(mod - 1) / Sz;
    for (int i = 1; i < Sz; ++i)
        R[i] = R[i >> 1] >> 1 | (i & 1) << (Bt - 1);
}

inline void FNTT(ll *A, int Ty)
{
    for (int i = 0; i < Sz; ++i)
        if (R[i] < i)
            swap(A[R[i]], A[i]);
    for (int j = 1, j2 = 2; j < Sz; j <<= 1, j2 <<= 1)
    {
        ll gn = qPow(~Ty ? G : iG, (mod - 1) / j2), g, X, Y;
        for (int i = 0, k; i < Sz; i += j2)
        {
            for (k = 0, g = 1; k < j; ++k, g = g * gn % mod)
            {
                X = A[i + k], Y = g * A[i + j + k] % mod;
                A[i + k] = (X + Y) % mod, A[i + j + k] = (X - Y) % mod;
            }
        }
    }
    if (!~Ty)
        for (int i = 0; i < Sz; ++i)
            A[i] = A[i] * InvSz % mod;
}

inline void PolyInv(ll *A, int N, ll *B)
{
    static ll tA[MS], tB[MS];
    B[0] = qPow(A[0], mod - 2);
    for (int L = 1; L < N; L <<= 1)
    {
        int L2 = L << 1, L4 = L << 2;
        InitFNTT(L4);
        memcpy(tA, A, 8 * L2);
        memset(tA + L2, 0, 8 * (Sz - L2));
        memcpy(tB, B, 8 * L);
        memset(tB + L, 0, 8 * (Sz - L));
        FNTT(tA, 1), FNTT(tB, 1);
        for (int i = 0; i < Sz; ++i)
            tB[i] = (2 - tB[i] * tA[i]) % mod * tB[i] % mod;
        FNTT(tB, -1);
        for (int i = 0; i < L2; ++i)
            B[i] = tB[i];
    }
}

inline void PolyLn(ll *A, int N, ll *B)
{
    static ll tA[MS], tB[MS];
    PolyInv(A, N - 1, tB);
    InitFNTT(N * 2 - 3);
    for (int i = 1; i < N; ++i)
        tA[i - 1] = i * A[i] % mod;
    memset(tA + N - 1, 0, 8 * (Sz - N + 1));
    memset(tB + N - 1, 0, 8 * (Sz - N + 1));
    FNTT(tA, 1), FNTT(tB, 1);
    for (int i = 0; i < Sz; ++i)
        tA[i] = tA[i] * tB[i] % mod;
    FNTT(tA, -1);
    B[0] = 0;
    for (int i = 1; i < N; ++i)
        B[i] = tA[i - 1] * Inv[i] % mod;
}

inline void PolyExp(ll *A, int N, ll *B)
{
    static ll tA[MS], tB[MS];
    B[0] = 1;
    for (int L = 1; L < N; L <<= 1)
    {
        int L2 = L << 1, L4 = L << 2;
        memset(B + L, 0, 8 * (L2 - L));
        PolyLn(B, L2, tB);
        InitFNTT(L4);
        memcpy(tA, B, 8 * L);
        memset(tA + L, 0, 8 * (Sz - L));
        for (int i = 0; i < L2; ++i)
            tB[i] = ((!i) - tB[i] + A[i]) % mod;
        memset(tB + L2, 0, 8 * (Sz - L2));
        FNTT(tA, 1), FNTT(tB, 1);
        for (int i = 0; i < Sz; ++i)
            tA[i] = tA[i] * tB[i] % mod;
        FNTT(tA, -1);
        for (int i = 0; i < L2; ++i)
            B[i] = tA[i];
    }
}

int N, K;
ll A[MS], B[MS];
map<int, int> counter;
int main()
{
    scanf("%d%d", &N, &K);
    Init(MS);
    for (int i = 1, ai, bi; i <= N; ++i)
    {
        scanf("%d%d", &ai, &bi);
        if (bi <= K)
        {
            counter[bi]++;
        }
        ll val = bi * (ai + 1ll);
        if (val <= K)
        {
            counter[val]--;
        }
    }
    ///公式部分==把復雜度降到嚴格klgk
    for (map<int, int>::iterator it = counter.begin(); it != counter.end(); it++)
    {
        ll val = it->first, cnt = it->second;
        for (ll j = 1; j <= K / val; ++j)
        {
            A[val * j] = (A[val * j] + cnt * Inv[j]) % mod;
        }
    }
    //
    PolyExp(A, K + 1, B);
    cout << (B[K] + mod) % mod << endl;
    return 0;
}
/*
Verdict: 100 points

Language: C++

CPU Time usage: 534 ms

Memory usage: 19.9 MB
*/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM