[CF1188E] Problem from Red Panda 题解

[CF1188E] Problem from Red Panda 题解

考虑每个位置的操作次数 ci,不难发现,i 气球最后的颜色个数 diai+cikci,如果存在 ci>0,那么我们总是可以把所有气球少操作一次,这样上式不变,不影响最后的序列,下文所有的操作序列都假设 minci=0

考虑原序列和操作序列的关系,断言:操作序列(假设后)和原序列一一对应

证明:
形式化地说,假设有操作序列 A,B,|A|=|B|,最后得到的气球序列为 ta,tb,欲证 A=Bta=tb

充分性是显然的,论证必要性:考察气球序列中的位置 i,有 tai=tbi,有 ai+Aik|A|=ai+Bik|B|,因为 |A|=|B|,所以 Ai=Bi,证毕。

得到这个之后,我们只需要计数操作序列即可,并且不用管算重的问题。

发现操作总数 s 总是 maxai,这是因为如果 s>maxai,再根据 minci=0 的假设,总是存在一个 i,使得 di<0,这非法,所以可以考虑枚举 s

考虑操作序列合法的充要条件,首先显然有 di=ai+ciks0,得到 cisaik,设这个下界是 bounds(i),此时我们有 s=cibounds(i),为了保证任意时刻都存在合法的操作,另一个条件是 ts,tboundt(i)

证明:
必要性显然。

关于充分性,考虑构造一组合法操作序列,不难发现,随着 t 的增大,boundt(i) 一定单调不降,对于一个时刻,一定有一部分操作是必须要放到一些位置上,为了满足下界的需求,剩余有一些操作是可以随便放的,所以每次 boundt(i) 的增加时,把本来可以随便放的操作放到增加的 boundt(i) 的位置即可,因为有 tboundt(i),所以总是能够满足 boundt(i) 的需求。

根据不等式 cisaik,如果 ais,则 i 位置不需要操作也行,它的 boundi=0,而 ai<s 的部分就有操作的需求,不妨假设 a 不降,随着 s 的增加,有操作需求的位置的分界线 r 会单调地向右移动,用双指针维护这个分界线,同样的,我们也可以维护 w=boundt(i),因为每次变化的只有 s1ai(modk) 的那些 i,它们的下界会增加 1,用桶维护 w

剩下的部分是一些简单的组合计数,需要满足:前 r 个位置的操作次数 bounds(i),ir,后 kr 个位置中至少有一个位置操作次数是 0(需要满足一开始的假设才不会算重),现在有 s 个操作次数,问有几种分配操作次数的方案。

容斥后 kr 至少有一个 0,然后就是经典小球盒子插板法解决的问题了。

这里的答案就等于:

(nw+k1k1)(nw+r1k1)

需要注意的是,如果一个时刻 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;
}

posted @   MoyouSayuki  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
:name :name
点击右上角即可分享
微信分享提示