luoguP4491&&bzoj5306[HAOI2018]染色 NTT 容斥原理 组合数学

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/85339211

[HAOI2018]染色

题目传送门
luogu
bzoj

分析

一个神奇的容斥。
题目中的恰好提示我们要把它转化成至少
f [ i ] f[i] 表示出现至少 i i 种出现次数为 S S 的颜色的方案数。
首先钦定 C m i C_m^i 个颜色。
其次钦定 C n i S C_n^{iS} 个位置。
把他们全排列(可重集的排列) i S ! ( S ! ) i \frac{iS!}{(S!)^i}
剩下 m i m-i 种颜色, n i S n-iS 个位置,可以瞎放 ( m i ) n i S (m-i)^{n-iS}
于是
f [ i ] = C m i C n i S i S ! ( S ! ) i ( m i ) n i S f[i]=C_m^iC_n^{iS}\frac{iS!}{(S!)^i}(m-i)^{n-iS}
注意我们只需要处理出 l i m = m i n ( n S , m ) lim=min(\frac{n}{S},m) f f
求出这个玩意儿可以通过线性处理阶乘和阶乘逆元做到 O ( l i m l o g ) O(limlog) 搞出来
剩下根据容斥原理。
A n s [ k ] = i = k ( 1 ) i k C i k f [ i ] Ans[k]=\sum_{i=k}(-1)^{i-k}C_i^kf[i]
那个 C i k C_i^k 是因为从每个 f i f_i 都被 f k f_k 重复计算了 C i k C_i^k 次。
接下来就是套路了。
A n s [ k ] = i = k ( 1 ) i k i ! k ! ( i k ) ! f [ i ] Ans[k]=\sum_{i=k}(-1)^{i-k}\frac{i!}{k!(i-k)!}f[i]
A n s [ k ] k ! = i = k ( 1 ) i k ( i k ) ! f [ i ] i ! Ans[k]\cdot k!=\sum_{i=k}\frac{(-1)^{i-k}}{(i-k)!}f[i]i!
N T T NTT 即可。

代码

#include<bits/stdc++.h>
const int N = 262144, M = 1e7 + 10, P = 1004535809;
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 w[N], R[N], A[N], B[N], sz[N], f[M], g[M], v[N], tp, L, InvL, n;
int fix(int x) {return (x >> 31 & P) + x;}
int add(int a, int b) {return a += b, a >= P ? a - P : a;}
int mul(int a, int b) {return 1LL * a * b % P;}
int Pow(int x, int k) {
    int r = 1;
    for(;k; x = mul(x, x), k >>= 1)
        if(k & 1)
            r = mul(r, x);
    return r;
}
int Inv(int x) {return Pow(x, P - 2);}
void Pre(int m) {
    L = 1; int x = 0;
    for(;(L <<= 1) < m; ++x) ;
    for(int i = 1;i < L; ++i)
        R[i] = R[i >> 1] >> 1 | (i & 1) << x;
    w[0] = 1; int wn = Pow(3, (P - 1) / L);
    for(int i = 1;i < L; ++i)
        w[i] = mul(w[i - 1], wn);
    InvL = Inv(L);
}
void DFT(int *F) {
    for(int i = 0;i < L; ++i)
        if(i < R[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 + j + i, *p = w, tp;
            for(int k = i; k--; ++l, ++r, p += d)
                tp = mul(*r, *p), *r = fix(*l - tp), *l = add(*l, tp);
        }
}
int C(int m, int n) {return mul(mul(f[m], g[n]), g[m - n]);}
void Get(int n) {
    f[0] = 1; for(int i = 1;i <= n; ++i) f[i] = mul(f[i - 1], i);
    g[n] = Inv(f[n]); for(int i = n;i; --i) g[i - 1] = mul(g[i], i);
}
int main() {
    int n = ri(), m = ri(), s = ri(), lim = std::min(n / s, m);
    for(int i = 0;i <= m; ++i) v[i] = ri();
    Get(std::max(n, m));
    for(int i = 0;i <= lim; ++i) {
        int cnt = i * s;
        A[i] = mul(mul(C(m, i), C(n, cnt)), mul(f[cnt], Pow(g[s], i)));
        A[i] = mul(A[i], mul(Pow(m - i, n - cnt), f[i]));
    }
    for(int i = 0;i <= lim; ++i)
        B[lim - i] = i & 1 ? fix(-g[i]) : g[i];
    Pre((lim << 1) + 1);
    DFT(A); DFT(B);
    for(int i = 0;i < L; ++i)
        A[i] = mul(A[i], B[i]);
    DFT(A);
    int ans = 0;
    for(int i = 0;i <= lim; ++i) {
        ans = add(ans, mul(mul(v[i], g[i]), mul(A[L - i - lim & L - 1], InvL)));
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/85339211
今日推荐