ABC453E Team Division 题解

原题链接

Description

有 \(N\) 个球员,编号 \(1\) 到 \(N\),要分成 可区分的 A 队和 B 队,每队至少一人。

对于每个球员 \(i\),给定一个约束区间 \([L_i, R_i]\),要求球员 \(i\) 所在队伍的总人数必须在 \([L_i, R_i]\) 内(保证 \(1 \le L_i \le R_i \le N-1\))。

求满足所有约束的分队方案数,结果模 \(998244353\)。

Solution

设 A 队的人数为 \(x\)(\(1 \le x \le N-1\)),显然 B 队人数就是 \(N-x\)。

设对于固定的 \(x\),满足约束的分队方案数为 \(f(x)\)。由于两队可区分,总的分队方案数是所有 \(f(x)\) 的和,即

\[\text{ans}=\sum_{i=1}^{N-1}f(i) \]

于是我们只需求出每个 \(f(x)\) 即可。

对于一个固定的 \(x\),每个球员的归属有四种可能:

  • 只能进 A:\(x \in [L_i, R_i]\) 且 \(x \notin [N-R_i, N-L_i]\)。

  • 只能进 B:\(x \notin [L_i, R_i]\) 且 \(x \in [N-R_i, N-L_i]\)。

  • 可进 A 或 B:\(x \in [L_i, R_i] \cap [N-R_i, N-L_i]\)。

  • 两队都不能进:\(x \notin [L_i, R_i]\) 且 \(x \notin [N-R_i, N-L_i]\)。

设对于给定的 \(x\):

  • \(\text{canA}(x) = \#\{i \mid x \in [L_i, R_i]\}\),表示能够加入 A 队的球员数。

  • \(\text{canB}(x) = \#\{i \mid x \in [N-R_i, N-L_i]\}\),表示能够加入 B 队的球员数。

  • \(\text{both}(x) = \#\{i \mid x \in [L_i, R_i] \cap [N-R_i, N-L_i]\}\),表示既能加入 A 队也能加入 B 队的球员数。

那么:

  • 只能去 A 的人数 \(\text{onlyA}(x) = \text{canA}(x) - \text{both}(x)\)

  • 只能去 B 的人数 \(\text{onlyB}(x) = \text{canB}(x) - \text{both}(x)\)

  • 两队都不能进的人数 \(\text{none}(x) = N - (\text{onlyA}(x) + \text{onlyB}(x) + \text{both}(x)) = N - (\text{canA}(x) + \text{canB}(x) - \text{both}(x))\)

接下来,根据 \(\text{onlyA}(x)\)、\(\text{onlyB}(x)\)、\(\text{both}(x)\) 和 \(\text{none}(x)\) 的取值分类讨论计算 \(f(x)\):

  • 如果 \(\text{none}(x)>0\),那么总有无队球员,不存在满足约束的组队方案,即 \(f(x)=0\)。

  • 如果 \(\text{none}(x)=0\),还需分三种情况:

    • \(x < \text{onlyA}(x)\),这说明 A 队的球员数必大于 \(x\),不存在满足约束的组队方案,\(f(x)=0\)。

    • \(N - x < \text{onlyB}(x)\),即 \(x > \text{both}(x) + \text{onlyA}(x)\),这说明 B 队的球员数必大于 \(N-x\),不存在满足约束的组队方案,\(f(x)=0\)。

    • \(0 \le x - \text{onlyA}(x) \le \text{both}(x)\),此时 A 队已经有了 \(\text{onlyA}(x)\) 个必须去 A 的人,还需要从 \(\text{both}(x)\) 个自由人中选 \(x - \text{onlyA}(x)\) 个加入 A 队(其余自动去 B 队)。因此方案数

\[f(x)=\dbinom{\text{both}(x)}{x - \text{onlyA}(x)} \]

由于 \(N\) 最大 \(2\times 10^5\),不能对每个 \(x\) 遍历所有球员,因此需要考虑如何高效计算 \(\text{canA}(x), \text{canB}(x), \text{both}(x)\)。

观察到每个球员的约束对应数轴上的一段区间,我们需要统计每个整数点被多少个区间覆盖,这可以用差分数组完成。

Code

#include <bits/stdc++.h>
typedef long long i64;
using namespace std;
const int MAXN = 2e5;
const i64 MOD = 998244353;

int N, canA[MAXN + 1], canB[MAXN + 1], both[MAXN + 1];
i64 fact[MAXN + 1], invfact[MAXN + 1];

i64 qpow(i64 x, i64 y) // 快速幂,用于求逆
{
    i64 res = 1;
    while (y)
    {
        if (y & 1)
            res = res * x % MOD;
        x = x * x % MOD;
        y >>= 1;
    }
    return res;
}

void precal() // 预处理 1 至 N 的阶乘及其逆
{
    fact[0] = 1;
    for (int i = 1; i <= N; ++i)
        fact[i] = fact[i - 1] * i % MOD;
    invfact[N] = qpow(fact[N], MOD - 2);
    for (int i = N - 1; i >= 1; --i)
        invfact[i] = invfact[i + 1] * (i + 1) % MOD;
    invfact[0] = 1; // 特别处理 0! 的逆
}

i64 C(i64 x, i64 y) // 计算组合数
{
    return fact[x] * invfact[y] % MOD * invfact[x - y] % MOD;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> N;
    precal();

    int L, R, l, r;
    for (int i = 1; i <= N; ++i) // 求差分数组
    {
        cin >> L >> R;
        ++canA[L];
        --canA[R + 1];
        ++canB[N - R];
        --canB[N - L + 1];
        l = max(L, N - R); // 求交区间的好写法
        r = min(R, N - L);
        if (l <= r)
        {
            ++both[l];
            --both[r + 1];
        }
    }

    for (int i = 1; i <= N; ++i) // 前缀和得到原数组
    {
        canA[i] += canA[i - 1];
        canB[i] += canB[i - 1];
        both[i] += both[i - 1];
    }

    i64 ans = 0;

    for (int x = 1; x <= N - 1; ++x) // 累加 f(x)
    {
        if (canA[x] + canB[x] - both[x] < N) // 实现时可把 onlyA 等代换掉
            continue;
        if (x < canA[x] - both[x] || x > canA[x])
            continue;
        ans = (ans + C(both[x], x - canA[x] + both[x])) % MOD;
    }

    cout << ans << '\n';
    return 0;
}

时间复杂度 \(\mathcal O(N)\),空间复杂度 \(\mathcal O(N)\)。

posted @ 2026-05-06 21:34  SHUddol  阅读(20)  评论(0)    收藏  举报