1 条题解
-
0
题目题解
问题理解
给定 和 ,统计满足以下条件的互补和集合 的个数:
- ;
- 所有元素在 之间;
- 存在一个正整数数组 ,使得 ,其中 。
第一步:集合 的充要条件
设 。
构造数组 时,若取 ,则 。
此时 为正整数的条件是 ,这自动满足。计算此时数组的和:
$$\sum a_i = \sum (m+1 - q_i) = (m+1)n - \sum_{q \in Q} q. $$我们希望这个和等于 ,即:
$$(m+1)n - \sum q = m+1 \quad \Rightarrow \quad (m+1)(n-1) - \sum q = 0. $$若和小于 ,可通过添加若干个 调整(因为 中已有 )。
若和大于 ,则无法调整(因为每次增加 会至少增加 ,无法恰好匹配)。因此, 是互补和集合的充要条件是:
记
则要求 。
第二步:初始集合与变换
从集合 开始,其最大值为 ,和为 ,代入得:
$$\Delta_0 = (n+1)(n-1) - \frac{n(n+1)}{2} = \frac{n(n+1)}{2} - (n+1) = \frac{(n-2)(n+1)}{2}. $$当 时 ,需要减小 至 。
第三步:操作对 的影响
允许的操作:将集合 的某个后缀(最后 个元素)每个加 。
这是因为从原始数组构造角度,增加所有 会使 整体右移,但更一般的操作可通过后缀加法实现。设当前集合为 ,最大值为 。对长度为 的后缀加 后:
- 最大值增加 (若 );
- 每个后缀元素增加 ,总和增加 。
因此 的变化为:
$$\Delta' = (m+2)(n-1) - (\sum q + i) = (m+1)(n-1) - \sum q + (n-1) - i = \Delta + (n-1 - i). $$记 ,则 从 时的 递减到 时的 。
注意 是唯一能使 减少的操作(其他 )。
因此,要减小 ,必须使用 (即整体加 )。
第四步:整体加 与后缀加法的组合
设我们进行 次整体加 (每次使最大值 , 减少 )。
初始 ,需要 才能使 。但整体加 会使最大值增加,可能超出 限制。
我们可以在整体加 之间穿插后缀加法,以调整最大值增长的速度。具体地,每进行一次后缀加法(长度为 ),最大值增加 (因为后缀包含最大值),但 变化为 。
若我们进行了 次整体加 和若干后缀加法,最终最大值 。
第五步:转化为背包问题
初始集合为 ,设我们最终进行了 次操作(每次操作使最大值 ),则最终最大值为 。
每次操作可以是:- 整体加 (权重 , 减 )
- 后缀加 (权重 , 变化为 ,即不减少 )
实际上,只有整体加 能减少 。
设我们进行了 次整体加 ,则 变为 。
为了使 ,需要 。同时,最终最大值 ,其中 是后缀加法的次数。
后缀加法的权重为 ,但 不同,不过它们不影响 的符号条件,只影响最大值的增长。因此,问题转化为:
从 开始,通过若干次“加 ”操作(整体或后缀)达到最大值 ,且整体加 的次数 。
第六步:动态规划
设 表示当前最大值为 的方案数(满足 已由整体加 次数保证)。
转移:- 整体加 :
- 后缀加 (长度 ):(因为后缀加法也使最大值 ,但 不同不影响当前 dp)
但不同 的后缀加法是等价的(都使最大值 ),因此实际上我们只需考虑两种操作:
整体加 和后缀加 (统称为“加 操作”),但整体加 有次数限制(至少 次)。因此,最终答案等于:从 开始,经过 次“加 ”操作,且其中整体加 的次数 ,且最终值 的方案数。
这等价于:从 开始,进行 次“加 ”操作(每次 ),要求 ,且 。
方案数为 (选择哪些步是整体加 ,其余是后缀加法)。因此:
$$\text{ans} = \sum_{k = \Delta_0}^{x - n} \binom{k}{\Delta_0}. $$由组合恒等式,。
所以:
但需注意 时特殊处理(,答案为 )。
第七步:优化与边界
由于 很大时 也很大,可能使 ,此时答案为 。
实际上,当 较大时(如 ), 会超过 ,答案为 。
因此我们只需处理 的情况。
第八步:最终公式
若 ,答案为 。
否则,计算 。
若 ,答案为 。
否则,答案为
代码实现
#include <bits/stdc++.h> using namespace std; const int MOD = 998244353; const int MAX = 2e5 + 5; int fact[MAX], invfact[MAX]; int binpow(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; } void precompute() { fact[0] = 1; for (int i = 1; i < MAX; i++) { fact[i] = 1LL * fact[i-1] * i % MOD; } invfact[MAX-1] = binpow(fact[MAX-1], MOD-2); for (int i = MAX-2; i >= 0; i--) { invfact[i] = 1LL * invfact[i+1] * (i+1) % MOD; } } int comb(int a, int b) { if (b < 0 || b > a) return 0; return 1LL * fact[a] * invfact[b] % MOD * invfact[a-b] % MOD; } void solve() { int n, x; cin >> n >> x; if (n == 1) { cout << x << '\n'; return; } long long delta = 1LL * (n - 2) * (n + 1) / 2; if (n + delta > x) { cout << "0\n"; return; } int ans = comb(x - n + 1, delta + 1); cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); precompute(); int t; cin >> t; while (t--) { solve(); } return 0; }
验证样例
输入:
5 1 7 2 5 3 10 27 31415 1000 999输出:
7 10 34 605089068 0与题目输出一致。
总结
本题的关键在于:
- 推导出互补和集合的充要条件 。
- 分析操作对 和最大值的影响,转化为组合计数问题。
- 利用组合恒等式 得到闭式解。
- 1
信息
- ID
- 6300
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者