IOI2020国家集训队作业 Part 2
这个坑通过不断skip能开到 part2 我是妹想到的。
CF627E Orchestra
- 给定大小 \(n\times m\) 的矩形,其中有 \(c\) 个黑色格子,其余为白色。
- 求黑格数 \(\geq k\) 的子矩形个数。
- \(n,m,c\leq 3000,k\leq 10\)。
对题目性质的理解更深一层。
首先不难发现有一个 \(O(n^2m)\) 的做法,即枚举上下两层并对列进行双指针。
这个做法并不基于 \(c\) 或 \(k\) 的大小,所以只能做到这一步。
稍加思考能得到一个分治做法,按照行列中较大的一维分治,每次可以 \(O(n^2k)\) 的求出跨过分治中线的矩形个数。
分析时间复杂度,设 \(T(n)\) 表示变成为 \(n\) 的正方形的复杂度,那么 \(T(n)=4T(n/2)+O(n^2k)\)。
作为主定理学傻了人,一眼过去直接认为是 \(O(n^2k)\) 的并喜提 TLE。
实际上复杂度分析确实没出问题,但是简单想想就发现常数不是一般的大。
不知道使用主定理估计分治算法复杂度的时候是否都要承担大常数的风险,但我的建议是还是用分治层数 $\times $ 第一层的处理复杂度来估计比较接近真实值,实现后发现常数大概有个 \(5\sim 10\) 倍,极限数据需要 \(10s\)。
其实这个结果也是预料的到的,因为这个做法并没有用到 \(c\) 很小的性质。
考虑根据黑格数量很少涉及一个相关算法,还是枚举上下两行,但是固定上边界后动态维护下边界移动时的答案。
考虑对在当前范围内的黑格按照 列升序为第一关键字,行升序为第二关键字 排序。
对每一列处理出离它最近的向左第 \(k\) 个格子,这一列作为右边界的贡献就是这个格子的纵坐标。
考虑并不在每个右边界处理贡献,而是统一加到第 \(k\) 个格子上。这样任意时刻都只有 \(O(c)\) 个需要维护的量。
发现删除一个格子的影响是 \(O(k)\) 的,仅仅是将前面 \(k\) 个格子的代价整体向左平移一格。
那么直接建立双向链表,倒序统计支持删除即可,复杂度 \(O(n^2k)\)。这一算法使用到了所有题目性质,所以拥有优秀的复杂度。
总而言之,只有充分利用题目的每一个性质,才能得到令人满意的算法。