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)\) 的和,即
于是我们只需求出每个 \(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 队)。因此方案数
-
由于 \(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)\)。

浙公网安备 33010602011771号