[CF1188E] Problem from Red Panda 題解

MoyouSayuki發表於2024-11-14

[CF1188E] Problem from Red Panda 題解

考慮每個位置的操作次數 \(c_i\),不難發現,\(i\) 氣球最後的顏色個數 \(d_i\)\(a_i + c_ik-\sum c_i\),如果存在 \(\forall c_i > 0\),那麼我們總是可以把所有氣球少操作一次,這樣上式不變,不影響最後的序列,下文所有的操作序列都假設 \(\min c_i = 0\)

考慮原序列和操作序列的關係,斷言:操作序列(假設後)和原序列一一對應

證明:
形式化地說,假設有操作序列 \(A, B, |A| = |B|\),最後得到的氣球序列為 \(ta, tb\),欲證 \(A = B \Longleftrightarrow ta = tb\)

充分性是顯然的,論證必要性:考察氣球序列中的位置 \(i\),有 \(ta_i = tb_i\),有 \(a_i + A_ik-|A| = a_i + B_ik - |B|\),因為 \(|A| = |B|\),所以 \(A_i = B_i\),證畢。

得到這個之後,我們只需要計數操作序列即可,並且不用管算重的問題。

發現操作總數 \(s\) 總是 \(\le \max a_i\),這是因為如果 \(s> \max a_i\),再根據 \(\min c_i = 0\) 的假設,總是存在一個 \(i\),使得 \(d_i < 0\),這非法,所以可以考慮列舉 \(s\)

考慮操作序列合法的充要條件,首先顯然有 \(\forall d_i = a_i + c_ik-s \ge 0\),得到 \(c_i\ge \lceil \dfrac{s - a_i}{k} \rceil\),設這個下界是 \(bound_s(i)\),此時我們有 \(s =\sum c_i \ge \sum bound_s(i)\),為了保證任意時刻都存在合法的操作,另一個條件是 \(\forall t\le s, t\ge\sum bound_t(i)\)

證明:
必要性顯然。

關於充分性,考慮構造一組合法操作序列,不難發現,隨著 \(t\) 的增大,\(bound_t(i)\) 一定單調不降,對於一個時刻,一定有一部分操作是必須要放到一些位置上,為了滿足下界的需求,剩餘有一些操作是可以隨便放的,所以每次 \(bound_t(i)\) 的增加時,把本來可以隨便放的操作放到增加的 \(bound_t(i)\) 的位置即可,因為有 \(t\ge\sum bound_t(i)\),所以總是能夠滿足 \(bound_t(i)\) 的需求。

根據不等式 \(c_i\ge \lceil \dfrac{s - a_i}{k} \rceil\),如果 \(a_i \ge s\),則 \(i\) 位置不需要操作也行,它的 \(bound_i = 0\),而 \(a_i < s\) 的部分就有操作的需求,不妨假設 \(a\) 不降,隨著 \(s\) 的增加,有操作需求的位置的分界線 \(r\) 會單調地向右移動,用雙指標維護這個分界線,同樣的,我們也可以維護 \(w = \sum bound_t(i)\),因為每次變化的只有 \(s - 1\equiv a_i\pmod k\) 的那些 \(i\),它們的下界會增加 1,用桶維護 \(w\)

剩下的部分是一些簡單的組合計數,需要滿足:前 \(r\) 個位置的操作次數 \(\ge bound_s(i), i\le r\),後 \(k - r\) 個位置中至少有一個位置操作次數是 0(需要滿足一開始的假設才不會算重),現在有 \(s\) 個操作次數,問有幾種分配操作次數的方案。

容斥後 \(k - r\) 至少有一個 0,然後就是經典小球盒子插板法解決的問題了。

這裡的答案就等於:

\[\binom{n - w + k - 1}{k - 1} - \binom{n - w + r - 1}{k - 1} \]

需要注意的是,如果一個時刻 \(s < w\),直接結束列舉,這是為了滿足充要條件 2。

程式碼實現

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
//#define int long long
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 2e6 + 10, mod = 998244353;

int k, a[N], fac[N], ifac[N], cnt[N], m;
int qmi(int a, int b) {
    int res = 1;
    while(b) {
        if(b & 1) res = 1ll * res * a % mod;
        a = 1ll * a * a % mod, b >>= 1;
    }
    return res;
}
int C(int n, int m) {
    if(n < m) return 0;
    return 1ll * fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> k;
    for(int i = 1; i <= k; i ++) cin >> a[i], m = max(m, a[i]);
    fac[0] = ifac[0] = 1;
    for(int i = 1; i < N; i ++) fac[i] = 1ll * fac[i - 1] * i % mod;
    ifac[N - 1] = qmi(fac[N - 1], mod - 2);
    for(int i = N - 2; i; i --) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
    int r = 0, w = 0;
    long long ans = 0;
    sort(a + 1, a + k + 1);
    for(int s = 0, j = 1; s <= m; s ++) {
        while(j <= k && a[j] < s) cnt[a[j] % k] ++, j ++;
        w += cnt[(s - 1 + k) % k];
        if(w > s) break;
        r = j - 1;
        (ans += C(s - w + k - 1, k - 1) - C(s - w + r - 1, k - 1)) %= mod;
    }
    cout << (ans + mod) % mod << '\n';

    return 0;
}

相關文章