[NOI Online #2 入門組] 建設城市(組合數學) | 錯題本

題目

[NOI Online #2 入門組] 建設城市

分析

正整數 x1,x2,,xnx_1, x_2, \cdots, x_n 滿足 1x1x2xnr1 \leq x_1 \leq x_2 \leq \cdots \leq x_n \leq r,則 x1,x2,,xnx_1, x_2, \cdots, x_n 的取值方案數爲 Cn+r1r1C_{n + r - 1}^{r - 1}。證明:將問題轉化爲構造一個序列 k1,k2,,krk_1, k_2, \cdots, k_r 表示 [1,r][1, r] 中每個數在 xx 中出現了多少次,則 kkxx 一一對應,我們只需要求 kk 的取值方案數,kk 需滿足的條件是 k1+k2++kr=nk_1 + k_2 + \cdots + k_r = nk1,k2,,kr[0,n]k_1, k_2, \cdots, k_r \in [0, n],插空法得 kk 的方案數爲 Cn+r1r1C_{n + r - 1}^{r - 1}

然後枚舉 x,yx, y 的高度計算方案數即可。

錯因

  • 想到 xx 轉化爲 kk 的時候考試僅剩十幾分鍾了,推出 Cn+r1r1C_{n + r - 1}^{r - 1} 的時候還有 3 分鐘考試結束;
  • 預處理階乘的時候要處理到 m+nm + n 而不是 nn(最後一分鐘過了第一個樣例第二個樣例出 0,如果多 3 分鐘就能發現這個問題但是沒有時間了,導致 100 pts \to 0 pts);

代碼

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>

const int MOD = 998244353;
const int MAXN = 100000;

int M, N, X, Y;
int Fac[2 * MAXN + 5], Inv[2 * MAXN + 5];

inline int Mul(const int &x, const int &y) {
    return (long long)x * y % MOD;
}

inline int Add(int x, const int &y) {
    x += y; if (x >= MOD) x -= MOD; return x;
}

int Pow(int x, int y) {
    int ret = 1;
    while (y) {
        if (y & 1)
            ret = Mul(ret, x);
        x = Mul(x, x);
        y >>= 1;
    }
    return ret;
}

int C(int n, int m) {
    return Mul(Fac[n], Mul(Inv[m], Inv[n - m]));
}

int Cal(int rgt, int n) {
    return C(n + rgt - 1, rgt - 1);
}

int main() {
    scanf("%d%d%d%d", &M, &N, &X, &Y);
    Fac[0] = 1;
    for (int i = 1; i <= M + N; i++)
        Fac[i] = Mul(Fac[i - 1], i);
    Inv[M + N] = Pow(Fac[M + N], MOD - 2);
    for (int i = M + N - 1; i >= 0; i--)
        Inv[i] = Mul(Inv[i + 1], i + 1);
    if (X > N) {
        int tmp = 2 * N - Y + 1;
        Y = 2 * N - X + 1;
        X = tmp;
    } // 通過這個處理把問題分成兩類: 1 <= x < y <= n; 1 <= x <= n < y <= 2n
    if (Y <= N) {
        int Ans = 0, R = Cal(M, N);
        for (int i = 1; i <= M; i++)
            Ans = Add(Ans, Mul(R, Mul(Cal(i, X - 1), Cal(M - i + 1, N - Y))));
        printf("%d", Ans);
    }
    else {
        int Ans = 0;
        for (int i = 1; i <= M; i++) {
//            printf("%d %d %d %d\n", Cal(i, X - 1), Cal(M - i + 1, N - X), Cal(i, 2 * N - Y), Cal(M - i + 1, Y - N - 1));
            Ans = Add(Ans, Mul(Mul(Cal(i, X - 1), Cal(M - i + 1, N - X)), Mul(Cal(i, 2 * N - Y), Cal(M - i + 1, Y - N - 1))));
        }
        printf("%d", Ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章