「CTS2019」珍珠

「CTS2019」珍珠

解题思路

看了好多博客才会,问题即要求有多少种方案满足数量为奇数的变量数 (leq n-2m)。考虑容斥,令 (F(k)) 为恰好有 (n) 个变量数量为奇数的方案数,(G(k)) 为钦点了 (k) 种变量的选法且它们数量都是奇数,剩下的变量随便组合的方案数。

那么,

[Ans = sum_{i=0}^{min(n-2m,D)} F(i) ]

显然 (F, G​) 之间满足以下关系:

[G(k) =sum_{i=k}^D {ichoose k} F(i) \ F(k) =sum_{i=k}^D {ichoose k}(-1)^{i-k}G(i) ]

第二个式子是一个经典卷积,所以只要求出 (G) 做多项式乘法就可以快速得到答案了。

考虑钦点的变量是有标号集合的拼接,用指数型生成函数 (frac{e^{x}-e^{-x}}{2}) 的形式表示单个变量数量为奇数的选法,(e^x) 表示剩下变量随便选的选法,于是

[G(k)=[x^n]{D choose k}(frac{e^x-e^{-x}}{2})^ke^{x(D-k)} \ =[x^n]frac{1}{2^k}{D choose k}(e^x-e^{-x})^ke^{x(D-k)} ]

二项式展开可以得到

[G(k)=[x^n]frac{1}{2^k}{D choose k}sum_{i=0}^k{k choose i}(-1)^ie^{x(k-2i)}e^{x(D-k)} \ =[x^n]frac{1}{2^k}{D choose k}sum_{i=0}^k{k choose i}(-1)^ie^{x(D-2i)} \ =frac{1}{2^k}{D choose k}sum_{i=0}^k{k choose i}(-1)^i(D-2i)^n ]

发现是一个卷积的形式,也只需要一遍多项式乘法。

code

/*program by mangoyang*/
#include <bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
	int ch = 0, f = 0; x = 0;
	for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
	for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
	if(f) x = -x;
}
const int N = 1 << 19, mod = 998244353, G = 3;
int a[N], b[N], js[N], inv[N], n, m, D, ans;
namespace poly{
    int rev[1<<22], len, lg;
    inline int Pow(int a, int b){
        int ans = 1;
        for(; b; b >>= 1, a = 1ll * a * a % mod)
            if(b & 1) ans = 1ll * ans * a % mod;
        return ans;
    }
    inline void timesinit(int lenth){
        for(len = 1, lg = 0; len <= lenth; len <<= 1, lg++);
        for(int i = 0; i < len; i++)
            rev[i] = (rev[i>>1] >> 1) | ((i & 1) << (lg - 1)); 
    }
    inline void dft(int *a, int sgn){
        for(int i = 0; i < len; i++)
            if(i < rev[i]) swap(a[i], a[rev[i]]);
        for(int k = 2; k <= len; k <<= 1){
            int w = Pow(G, (mod - 1) / k);
            if(sgn == -1) w = Pow(w, mod - 2);
            for(int i = 0; i < len; i += k){
                int now = 1;
                for(int j  = i; j < i + (k >> 1); j++){
                    int x = a[j], y = 1ll * a[j+(k>>1)] * now % mod;
                    a[j] = x + y >= mod ? x + y - mod : x + y;
                    a[j+(k>>1)] = x - y < 0 ? x - y + mod : x - y;
                    now = 1ll * now * w % mod;
                }
            }
        }
        if(sgn == -1){
            int Inv = Pow(len, mod - 2);
            for(int i = 0; i < len; i++) a[i] = 1ll * a[i] * Inv % mod;
        }
    }
}
using poly::timesinit;
using poly::Pow;
using poly::dft;
int main(){
    read(D), read(n), read(m);
    js[0] = 1, inv[0] = 1;
    for(int i = 1; i <= D; i++){
        js[i] = 1ll * js[i-1] * i  % mod; 
        inv[i] = Pow(js[i], mod - 2);
    }
    for(int i = 0; i <= D; i++){
        a[i] = 1ll * inv[i] * Pow((D - 2 * i + mod) % mod, n) % mod;
        if(i & 1) a[i] = mod - a[i];
        b[i] = inv[i];
    }
    timesinit(D + D + 1);
    dft(a, 1), dft(b, 1);
    for(int i = 0; i < poly::len; i++) 
        a[i] = 1ll * a[i] * b[i] % mod;
    dft(a, -1);
    for(int i = D + 1; i < poly::len; i++) a[i] = 0;
    for(int i = 0; i <= D; i++){
        a[i] = 1ll * a[i] * Pow(Pow(2, i), mod - 2) % mod;
        a[i] = 1ll * a[i] * js[D] % mod * inv[i] % mod * inv[D-i] % mod;
        a[i] = 1ll * a[i] * js[i] % mod;
        a[i] = 1ll * a[i] * js[i] % mod;
        if(i & 1) a[i] = (mod - a[i]) % mod;
    }
    reverse(a, a + D + 1);
    dft(a, 1);
    for(int i = 0; i < poly::len; i++) 
        a[i] = 1ll * a[i] * b[i] % mod;
    dft(a, -1);
    for(int i = 0; i <= min(n - 2 * m, D); i++){
        int x = 1ll * a[D-i] * inv[i] % mod;
        if(i & 1) x = (mod - x) % mod;
        ans = (ans + x) % mod;
    }
    cout << ans << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/mangoyang/p/11200776.html