【題解】HAOI2018染色

  好坑啊不開心……c++

  其實這題的想法仍是比較簡單粗暴的。題目明示剛好xxx,顯然排除斜率二分這個玩意兒,那麼不就只剩下容斥了嘛……spa

  令 \(A_{x}\) 爲剛好出現了 \(S\) 次的至少有 \(x\) 種的方案數, \({B_{x}}\) 爲剛好出現了\(S\) 次的顏色剛好 \(x\) 種的方案數。\(A_{x}\) 能夠 \(O(1)\) 求得,\(A_{x} = \frac{\binom{n}{S * x} * (m - x) ^ {n - S * x} * (S * i)!}{(S!)^{x}}\)。(爲了方便描述,咱們在下面所說的顏色均爲知足剛好出現 \(S\) 次的顏色)。code

那麼,一個剛好有 \(x\) 種顏色的方案對答案的貢獻應該爲blog

(咱們先只考慮顏色種數剛好爲 \(k\) 的)get

\(g_{x} = [x = k] = \sum_{i = 0}^{x}\binom{x}{i}f_{i}\)it

(\(f_{i}\) 爲容斥係數)class

二項式反演,得變量

\(f_{x} = \binom{k}{x} * (-1) ^ {k - x}\)im

因此令 \(B_{x}\) 爲剛好 \(x\) 種顏色的方案數,有:di

\(B_{x} = \sum_{i = 0}^{m}\binom{m}{i}*A_{i}*\binom{i}{x}*(-1)^{i - x}\)

因爲有 \(m\) 個\(B_{x}\) 要求值,且明顯的給了一個NTT模數,

考慮轉化成卷積的形式:

(把與各類變量相關的儘可能整理到一塊兒)

獲得:\(B_{x} = \frac{(-1)^{-x}}{x!}\sum_{i = 0}^{m}\frac{\binom{m}{i}*(-1)^{i}}{(i - x)!}\)

前面的是個常數,後面的是一個卷積(把 \(i - x\) 先 \(+ m\) 再反轉(防止爆負))……上NTT就行了。

  但這還沒完!預處理逆元、階乘逆元、階乘的話會容易TLE!因此應該快速冪暴力處理逆元……(;′⌒`)

#include <bits/stdc++.h>
using namespace std;
#define maxn 10000005
#define mod 1004535809
#define int long long
int n, m, M, S, W[maxn], G[maxn], a[maxn], b[maxn], c[maxn];
int ans, K, fac[maxn], A[maxn], B[maxn];
int lim = 1, len, rev[maxn];

int read()
{
    int x = 0, k = 1;
    char c; c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

int Up(int x) { if(x >= mod) x -= mod; return x; }
int Down(int x) { if(x < 0) x += mod; return x; }
int Mod(int x) { x %= mod; if(x < 0) x += mod; return x; }
int Qpow(int x, int timer)
{
    int base = 1;
    for(; timer; timer >>= 1, x = x * x % mod)
        if(timer & 1) base = x * base % mod;
    return base;
}
#define inv(x) Qpow(x, mod - 2)

int C(int n, int m)
{
    if(n < m || n < 0 || m < 0) return 0;
    int t = fac[n] * inv(fac[m]) % mod * inv(fac[n - m]) % mod;
    return t;
}

void init()
{
    fac[0] = 1;
    int t = max(n, m);
    for(int i = 1; i <= t; i ++) fac[i] = fac[i - 1] * i % mod;
}

void Pre()
{
    for(int i = 0; i <= m; i ++)
    {
        if(S * i > n) break;
        int t = S * i, t1 = C(n, t) * fac[t] % mod;
        A[i] = t1 * Qpow(inv(fac[S]), i) % mod * Qpow(m - i, n - t) % mod;
    }
    for(int i = 0; i <= m; i ++) 
        a[i] = C(m, i) * A[i] % mod * fac[i] % mod * Qpow(-1, i) % mod;
    for(int i = -m; i <= m; i ++) c[i + K] = i < 0 ? 0 : inv(fac[i]);
    for(int i = 0; i <= M; i ++) b[i] = c[M - i];
}

void NTT(int *A, int opt)
{
    int t = opt < 0 ? Qpow(3, mod - 2) : 3;
    for(int i = 0; i < lim; i ++) 
        if(i < rev[i]) swap(A[i], A[rev[i]]);
    
    for(int l = 1; l < lim; l <<= 1)
    {    
        int g = Qpow(t, (mod - 1) / (l << 1));
        for(int i = 1; i < l; i ++) G[i] =  1ll * G[i - 1] * g % mod; 
        for(int i = 0; i < lim; i += (l << 1))
            for(int j = i; j <= i + l - 1; j ++)
            {
                int x = A[j], y = 1ll * G[j - i] * A[j + l] % mod;
                A[j] = Up(x + y); A[j + l] = Down(x - y);
            }
    }
}

signed main()
{
    n = read(), m = read(), S = read(); K = m, M = m * 2 + 1;
    for(int i = 0; i <= m; i ++) W[i] = read();
    init(), Pre(); G[0] = 1;
    while(lim <= M + m) lim <<= 1, len ++;
    for(int i = 0; i < lim; i ++) 
        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << ((len - 1)));
    NTT(a, 1), NTT(b, 1);
    for(int i = 0; i < lim; i ++) a[i] = a[i] * b[i] % mod;
    NTT(a, -1); int inv1 = Qpow(lim, mod - 2);
    for(int i = 0; i <= m; i ++) 
    {
        int t =  ((i & 1) ? -1 : 1) * inv(fac[i]) % mod * W[i] % mod;
        ans = Mod(ans + a[i + m + 1] * inv1 % mod * t % mod);
    }
    printf("%lld\n", ans);
    return 0;
}
相關文章
相關標籤/搜索