1 条题解
-
0
问题分析
我们有:
- 个知识点()
- 每个知识点有 道题()
- 每道题有 ,表示算法和数据结构能力提升(可正可负)
- 对每个学生 ,必须从知识点 选恰好 道题
- 目标是最大化 ,其中:$$A = \sum_{i=1}^n \sum_{j \in S_i} a_{i,j}, \quad D = \sum_{i=1}^n \sum_{j \in S_i} d_{i,j} $$ 是第 个知识点选出的题号集合,且
思路推导
1. 最大化的几何意义
是向量 长度的平方。
我们固定 的取值范围,问题等价于:在所有可能的 中,选离原点最远的点。
2. 对每个知识点单独考虑
对于知识点 ,要从 道题中选 道题,所有可能的 之和构成一个集合。
这个集合可能很大,但是我们可以用背包 DP 求出来:
设 表示从该知识点中选 道题,能否得到 。
由于 范围很大(),直接枚举 不现实。
3. 关键观察:凸包优化
我们要求的是 ,即最大化向量模长。
对于给定的一个点集,模长最大的点一定在点集的凸包上(二维情况下显然,因为凸包外的点一定可以用凸包上的点“向外”推得更远)。因此,对于每个知识点 和选 道题的情况,我们先求出所有可能的 的凸包。
凸包上的点数是 的,比总可能点数 小得多。
4. 如何求凸包
对固定的 ,我们先用二维背包 DP 枚举所有可能 ,但只需要记录可达性,并找出所有可达的点。
具体做法:- 设 表示选 道题,得到的 时, 的所有可能值集合。
- 由于 值范围大,集合用 bitset 存储(bitset<最大值偏移>)。
- 这样 ,但 太大?不能直接做。
需要另一种方法:
因为 ,选 道题,可能的 点数最多 种,仍然很大,但凸包点数是 。一个更聪明的做法:
用所有可能的点直接求凸包不可行(点太多),我们改为用DP 生成凸包:- 对于每个知识点 ,对每个 ,直接枚举所有 选 的组合,计算 ,存下这些点,然后求凸包。
枚举 复杂度太高( 太大)。 - 但 ,,我们不需要对每个 都求,而是对于每个 ,我们只需对 这些 值,分别求出选 道题时的凸包点集。
这里可以用Meet-in-the-middle(折半搜索):- 将 道题分成两半(各约 )
- 分别枚举两半的所有子集( 仍然太大,不行)
所以必须用背包 DP + 凸包合并:
5. 凸包合并
如果我们已经知道知识点 选 道题的所有可能点集 (只保留凸包点),那么对于学生 ,需要选 道题从每个 ,我们要求:
$$(A,D) = \sum_{i=1}^n (A_i,D_i), \quad (A_i,D_i) \in P_{i, c_{k,i}} $$的最大 。
这相当于 个凸包的和(Minkowski 和)的最大模长点。
6. Minkowski 和的性质
两个凸包 的 Minkowski 和 的凸包,是它们各自凸包的 Minkowski 和,且新凸包的顶点由 和 的顶点按极角排序后依次相加得到。
因此我们可以:
- 对每个 求凸包,按极角排序。
- 依次合并凸包( 很小,所以合并 次可以接受)。
- 在最终凸包上找离原点最远的点(即最大 )。
7. 求每个 的凸包
这里需要高效求出 。
用DP 记录所有可达的 仍然困难(值域大)。
但 ,我们可以用费用流思想?
不,这是子集选取问题,可以用折半枚举 + 双指针求凸包:- 将 道题分成两半 和 (各 )
- 枚举 中选 道题的所有子集,计算 ,存到数组 。
- 枚举 中选 道题的所有子集,计算 ,存到数组 。
- 对所有 ,将 和 中的点两两相加,得到总点集,再求凸包。
但 的大小是 ,仍然可能很大( 时很大)。
不过 ,总合并次数不多,且 随机均匀,所以实际组合数不会达到最大,可能可行。但更稳妥的做法:用 DP 存储所有 的凸包而不是所有点。
8. 凸包 DP 设计
对于固定的知识点 ,我们定义 为选 道题的所有 的凸包(点集)。
$$dp_{c}' = \text{ConvexHull}(dp_{c} \cup \{ p + (a,d) \mid p \in dp_{c-1} \}) $$
初始 。
转移:加入一道题 :这个凸包合并可以用 Minkowski 和求凸包的方法。
但直接做复杂度: 可能较大,但凸包大小 ,所以 ,对于 太大。
9. 利用值随机性质
题目提示 随机均匀生成,所以 不会极端靠近 或 ,平均 。
但我们仍需要可靠算法。考虑折半枚举 + 凸包合并: 对每个 和 ,我们真的需要枚举所有组合吗?
我们可以枚举左半部分选 道的所有可能 ,求其凸包 ;右半部分选 道的凸包 ;
然后 的凸包就是这两个凸包的 Minkowski 和。
再对所有 的结果取凸包并集,得到 。这样 的大小是 , 大小 ,Minkowski 和求凸包 ,总复杂度 对每个 ,可以接受。
10. 最终步骤
- 预处理:
- 对每个知识点 ,对每个 ,用折半法求 (凸包点集,按极角排序)。
- 回答询问:
- 对于学生 ,取 。
- 用 Minkowski 和依次合并 ,得到总凸包 。
- 在 上找 最大的点。
- 因为 是凸多边形,最大模长点一定在凸包顶点上,直接枚举顶点即可。
复杂度分析
- 预处理:,,勉强可过。
- 询问:$q \cdot n \cdot (\text{凸包大小}) \le 666 \cdot 6 \cdot O(m) \approx 2.6 \times 10^6$,可以。
实现细节
- 用
__int128或BigInteger存 ,避免溢出。 - 凸包算法用 Andrew 单调链 。
- Minkowski 和:两个凸包 已按极角排序,则 的凸包可通过归并得到。
- 注意去重和边界情况。
总结
本题的核心在于:
- 将最大化 转化为在凸包上找最远点。
- 对每个知识点、每个选题数量求凸包,用折半枚举 + Minkowski 和高效计算。
- 合并各知识点的凸包得到总凸包,求最大模长。
利用凸包性质和 Minkowski 和,将看似 的枚举问题降为 的可解问题。
- 1
信息
- ID
- 5953
- 时间
- 1500ms
- 内存
- 2048MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者