[BZOJ4559][JLoi2016]成績比較(容斥+組合數學+拉格朗日插值)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=4559

Solution

由容斥原理得:

ans=i=Kmin{N1R}(1)iKCiKCN1ij=1MCN1iNRjix=1UixNRj(Ujx)Rj1

這麼長的式子,一項一項地進行解析:
(1) i=Kmin{N1R}(1)iKCiK :求「恰好完虐 K 個同學」時乘上容斥係數。
(2) CN1i :在 N1 個同學中選出 i 個同學被完虐。
(3) j=1M :把每一科分開處理之後相乘。
(4) CN1iNRji :在沒有被完虐的 N1i 個同學中,選出 NRji 個同學,讓他們第 j 課被虐。
(5) x=1Ui :枚舉 B 神第 j 科成績。
(6) xNRj :第 j 科被虐的同學的成績方案數。
(7) (Ujx)Rj1 :第 j 科沒有被虐的同學的方案數。
顯然,我們要處理的關鍵在於 x=1Ui 及之後的式子。
發現 (Ujx)Rj1 不好處理。
考慮無腦展開:
x=1UixNRjk=0Rj1(1)kxkUjRj1k

=k=0Rj1(1)kUjRj1kx=1UixNRj+k

注意到 x=1UixNRj+k 是以 x 爲自變量的 NRj+k+1 次多項式,上拉格朗日插值法即可求得。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 111, ZZQ = 1e9 + 7;
int pwe[N][N], y[N], qaq[N], tmp[N], rps[N], inv[N], com[N][N],
C[N][N], n, m, K, U[N], R[N], ans, czk[N];
int qpow(int a, int b) {
    int res = 1;
    while (b) {
        if (b & 1) res = 1ll * res * a % ZZQ;
        a = 1ll * a * a % ZZQ;
        b >>= 1;
    }
    return res;
}
void lagrange(int n) {
    int i, j;
    For (i, 1, n + 2)
        y[i] = (y[i - 1] + pwe[i][n]) % ZZQ;
    qaq[0] = 1;
    For (i, 1, n + 2) {
        tmp[0] = qaq[i] = 0;
        For (j, 0, i - 1) tmp[j + 1] = qaq[j];
        For (j, 0, i)
            qaq[j] = (tmp[j] - 1ll * qaq[j] * i % ZZQ + ZZQ) % ZZQ;
    }
    For (i, 1, n + 2) {
        For (j, 0, n + 2) tmp[j] = qaq[j];
        Rof (j, n + 1, 0) rps[j] = tmp[j + 1],
            tmp[j] = (tmp[j] + 1ll * tmp[j + 1] * i % ZZQ) % ZZQ;
        int sp = 1;
        For (j, 1, i - 1) sp = 1ll * sp * inv[j] % ZZQ;
        For (j, 1, n - i + 2)
            sp = 1ll * sp * (ZZQ - inv[j]) % ZZQ;
        For (j, 0, n + 1)
            com[n][j] = (com[n][j] + 1ll * y[i] *
            rps[j] % ZZQ * sp % ZZQ) % ZZQ;
    }
}
void init() {
    int i, j; inv[1] = 1;
    For (i, 2, 101) inv[i] = 1ll * (ZZQ - ZZQ / i) * inv[ZZQ % i] % ZZQ;
    For (i, 0, 102) {
        pwe[i][0] = 1;
        For (j, 1, 100) pwe[i][j] = 1ll * pwe[i][j - 1] * i % ZZQ;
    }
    For (i, 0, 100) lagrange(i);
    For (i, 0, 100) C[i][0] = 1;
    For (i, 1, 100) For (j, 1, i)
        C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % ZZQ;
}
int getval(int n, int k) {
    int i, x = 1, res = 0;
    For (i, 0, k + 1) res = (res + 1ll * com[k][i] * x % ZZQ) % ZZQ,
        x = 1ll * x * n % ZZQ;
    return res;
}
int main() {
    int i, j, k, dalao;
    init();
    dalao = n = read(); m = read(); K = read();
    For (i, 1, m) U[i] = read();
    For (i, 1, m) R[i] = read();
    For (i, 1, m) dalao = min(dalao, n - R[i]);
    For (j, 1, m) For (k, 0, R[j] - 1) {
        int miu = k & 1 ? ZZQ - 1 : 1, delta;
        delta = 1ll * C[R[j] - 1][k] *
            qpow(U[j], R[j] - 1 - k) % ZZQ
            * getval(U[j], n + k - R[j]) % ZZQ;
        czk[j] = (czk[j] + 1ll * miu * delta % ZZQ) % ZZQ;
    }
    For (i, K, dalao) {
        int miu = i - K & 1 ? ZZQ - 1 : 1,
            delta = 1ll * C[n - 1][i] * C[i][K] % ZZQ, pyz = 1;
        For (j, 1, m)
            pyz = 1ll * pyz * C[n - 1 - i][n - R[j] - i] % ZZQ,
            pyz = 1ll * pyz * czk[j] % ZZQ;
        ans = (ans + 1ll * miu * delta % ZZQ * pyz % ZZQ) % ZZQ;
    }
    cout << ans << endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章