1 条题解
-
0
A. Submission is All You Need —— 详细题解
题目重述
你有一个多重集 ,包含 个非负整数。初始得分为 。可以执行以下两种操作任意次(顺序任意):
- 选择当前 的一个子集 ,将 加到得分,然后删除
- 选择当前 的一个子集 ,将 加到得分,然后删除
求能获得的最大可能得分。
其中:
- = 中所有元素的和
- = 不在 中的最小非负整数
数据范围:
关键观察
观察 1:数值 的特殊性
对于只包含 的子集:
- ,加 分没有意义
- ,无论有多少个 ,一次操作就能得 分
因此,所有 应该通过一次 操作处理,得 分。
观察 2:数值 的处理
对于只包含 的子集(假设 ):
- ,得 分
- (因为 不在集合中),得 分
显然,用 操作更好。
观察 3:组合操作
对于包含多个不同数值的子集:
- 如果包含 , 可能得到 或更大的值,但不会超过 (因为 通常也存在)
- 实际上,把数值分开处理总是最优的
最优策略
- 处理 :如果存在 ,用一次 操作取走所有 ,得 分
- 处理 :对每个数值 ,用一次 操作取走所有值为 的元素,得 分
因此:
$$\text{ans} = [\text{cnt}[0] > 0] + \sum_{x=1}^{50} x \cdot \text{cnt}[x] $$
正确性证明
为什么分开处理是最优的?
-
任何包含 和其他数的子集, 至少为 ,但最多不会超过 (因为 要么存在要么不存在)。而用 处理其他数能获得更多分。
-
对于 ,如果混在一起用 ,得到的是它们的和,等于分开用 的和。分开处理不会损失分数。
-
如果试图用 处理 ,只能得到 或 分,远小于 。
-
因此,最优策略就是:
- 把所有 打包,用一次 得 分
- 把每个正数分别打包,用 得自身分数
代码实现
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n; cin >> n; vector<int> cnt(51, 0); // 统计每个数出现次数 for (int i = 0; i < n; i++) { int x; cin >> x; cnt[x]++; } long long ans = 0; // 如果有 0,用一次 mex 操作得 1 分 if (cnt[0] > 0) ans++; // 处理正数:用 sum 操作 for (int x = 1; x <= 50; x++) { ans += (long long)x * cnt[x]; } cout << ans << '\n'; } return 0; }
复杂度分析
- 时间复杂度: 每个测试用例
- 空间复杂度:
样例验证
样例 1
n=3, S={0,1,1} cnt[0]=1 → ans += 1 cnt[1]=2 → ans += 1×2 = 2 总得分 = 3 ✓样例 2
n=3, S={1,2,3} cnt[0]=0 → ans += 0 cnt[1]=1 → ans += 1 cnt[2]=1 → ans += 2 cnt[3]=1 → ans += 3 总得分 = 6 ✓
总结
要点 说明 核心思想 贪心: 用 ,正数用 时间复杂度 每个测试用例 难度 *800,约 3.0/10 分
- 1
信息
- ID
- 6534
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者