[CTS2019]珍珠 NTT 生成函數

[CTS2019]珍珠

題目傳送門:
luogu

分析

考慮每種顏色有幾個。假設已經求出了每種顏色的個數爲d1,d2dDd_1,d_2\cdots d_D
方案數就是n!d1!d2!dD!\frac{n!}{d_1!d_2!\cdots d_D!}
考慮這個方案合法的要求。
di(mod  2)n2m\sum d_i (\mod 2) \le n-2m
這是個蠻顯然的轉化,因爲相同顏色兩兩匹配,那麼不能匹配的就是奇數個的哪一個。
考慮求出gkg_kdi(mod  2)=k\sum d_i (\mod 2)=k的方案數,答案就是x=0n2mgk\sum_{x=0}^{n-2m} g_k
容易想到EGF。
我們考慮隨便放DD個隨機變量的方案數的EGF表示方法:
Dn=xneDxn!D^n=\lfloor x_n\rfloor e^{Dx}n!
什麼意思呢?
ex=i=0xii!e^x=\sum_{i=0}^{\infty}\frac{x^i}{i!},也就是說當前顏色可以放1,2,1,2,\cdots個,並且消除內部順序的影響,那麼放置DD個實際上就是卷積DD次後的nn次冪係數。
如今我們知道有kk個要放置的位置是奇數個的。
如何表示只放置奇數個?
i=0x2i(2i)!=exex2\sum_{i=0}^{\infty}\frac{x^{2i}}{(2i)!}=\frac{e^x-e^{-x}}{2}
偶數個同理爲ex+ex2\frac{e^x+e^{-x}}{2}
然後再用CDkC_D^k選出這若干個位置。
因此可以得到
gk=n!xnCDk(exex2)k(ex+ex2)Dkg_k=n!\lfloor x_n\rfloor C_D^k(\frac{e^x-e^{-x}}{2})^k (\frac{e^x+e^{-x}}{2})^{D-k}
xn\lfloor x_n\rfloor這個東西表示取第nn項係數
這個東西實際上可以直接推,但是比較麻煩。我們退而求其次,先求至少kk個奇數的答案:
fk=n!xnCDk(exex2)ke(Dk)xf_k=n!\lfloor x_n\rfloor C_D^k(\frac{e^x-e^{-x}}{2})^k e^{(D-k)x}
然後可以得到fi=j=iDCjigjf_i=\sum_{j=i}^DC_j^ig_j
二項式反演一下可以得到gi=j=iD(1)jiCjifjg_i=\sum_{j=i}^D(-1)^{j-i}C_j^if_j
這個東西可以拆組合數用NTTNTT解決。
然後問題轉化爲求ff
暴力二項式展開:(exex)k=i=0kCki(1)ieixe(ki)x(e^x-e^{-x})^k=\sum_{i=0}^kC_k^i(-1)^ie^{-ix}e^{(k-i)x}
然後得到
fk=n!xnCDk2ki=0kCki(1)ie(k2i)xe(Dk)x=n!xnCDk2ki=0kCki(1)ie(D2i)xf_k=n!\lfloor x_n\rfloor C_D^k2^{-k}\sum_{i=0}^kC_k^i(-1)^ie^{(k-2i)x}e^{(D-k)x}=n!\lfloor x_n\rfloor C_D^k2^{-k}\sum_{i=0}^kC_k^i(-1)^ie^{(D-2i)x}
注意到
e(D2i)xxn=(D2i)nn!e^{(D-2i)x}\lfloor x_n\rfloor=\frac{(D-2i)^n}{n!}
所以fk=n!CDk2ki=0kCki(1)i(D2i)nn!=CDk2kk!i=0k1(ki)!(1)i(D2i)ni!f_k=n!C_D^k2^{-k}\sum_{i=0}^kC_k^i(-1)^i\frac{(D-2i)^n}{n!}=C_D^k2^{-k}k!\sum_{i=0}^k\frac{1}{(k-i)!}(-1)^i\frac{(D-2i)^n}{i!}
\sum前面的那坨只和kk有關係不用管,後面是多項式(1)i(D2i)ni!xi(-1)^i\frac{(D-2i)^n}{i!}x^i1i!xi\frac{1}{i!}x^i卷積結果的第kk項。
NTTNTT即可
難點在於:1.問題的轉化。2.指數型生成函數的轉化。3.二項式反演的簡化。
其實我覺得第3個是最難的,因爲很容易直接走上直接肝式子的不歸路(雖然有人肝出來了)。前面兩個比較套路吧,主要還是得有模型積累。

代碼

#include<bits/stdc++.h>
const int N = 262144, P = 998244353;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f  = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int A[N], B[N], R[N], f[N], fac[N], ivf[N], w[N], L, D, n, m, InvL;
int Pow(int x, int k) {
    int r = 1;
    for(;k; k >>= 1, x = 1LL * x * x % P)
        if(k & 1)
            r = 1LL * r * x % P;
    return r;
}
int C(int m, int n) {return 1LL * fac[m] * ivf[n] % P * ivf[m - n] % P;}
void Pre(int m) {
    int x = 0; L = 1;
    for(;(L <<= 1) <= m; ++x) ;
    for(int i = 1;i < L; ++i)
        R[i] = R[i >> 1] >> 1 | (i & 1) << x;
    int wn = Pow(3, (P - 1) / L); w[0] = 1;
    for(int i = 1;i < L; ++i)
        w[i] = 1LL * w[i - 1] * wn % P;
    InvL = Pow(L, P - 2);
}
void NTT(int *F) {
    for(int i = 0;i < L; ++i)
        if(R[i] > i)
            std::swap(F[i], F[R[i]]);
    for(int i = 1, d = L >> 1; i < L; i <<= 1, d >>= 1)
        for(int j = 0;j < L; j += i << 1) {
            int *l = F + j, *r = F + i + j, *p = w, tp;
            for(int k = i; k--; ++l, ++r, p += d)
                tp = 1LL * *p * *r % P, *r = (*l - tp) % P, *l = (*l + tp) % P;
        }
}
int main() {
    D = ri(); n = ri(); m = ri();
    if(n < (m << 1)) return puts("0"), 0;
    if(D <= n - (m << 1)) return printf("%d\n", Pow(D, n)), 0;
    fac[0] = 1;
    for(int i = 1;i <= D; ++i)
        fac[i] = 1LL * fac[i - 1] * i % P;
    ivf[D] = Pow(fac[D], P - 2);
    for(int i = D; i; --i)
        ivf[i - 1] = 1LL * ivf[i] * i % P;
    for(int i = 0, w = 1;i <= D; ++i, w = -w)
        A[i] = 1LL * w * Pow(D - (i << 1), n) * ivf[i] % P, B[i] = ivf[i];
    Pre(D << 1);
    NTT(A); NTT(B);
    for(int i = 0;i < L; ++i)
        A[i] = 1LL * A[i] * B[i] % P;
    NTT(A);
    for(int i = 0;i <= D; ++i)
        f[i] = 1LL * A[L - i & L - 1] * InvL % P * C(D, i) % P * fac[i] % P * Pow(2, P - 1 - i) % P;
    for(int i = 0, w = 1;i <= D; ++i, w = -w)
        A[i] = 1LL * f[D - i] * fac[D - i] % P, B[i] = w * ivf[i];
    for(int i = D + 1; i < L; ++i)
        A[i] = B[i] = 0;
    NTT(A); NTT(B);
    for(int i = 0;i < L; ++i)
        A[i] = 1LL * A[i] * B[i] % P;
    NTT(A); long long ans = 0;
    for(int i = 0;i <= n - (m << 1); ++i) {
        int g = 1LL * A[L - (D - i) & L - 1] * InvL % P * ivf[i] % P;
        ans += g;
    }
    printf("%d\n", (ans % P + P) % P);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章