Ynoi 做题记录
https://www.luogu.com.cn/training/9410
我是仙人掌
发现一共只有 \(n^2\) 个点对,询问时的形式类似集合并,考虑使用 bitset.
由于询问与距离有关,因此考虑将 \(x\) 与 \(dis\) 作为两个维度,bitset 维护到 \(x\) 距离小于等于 \(dis\) 的点集.
预处理时使用 BFS 直接处理,查询时暴力合并 bitset.
时间复杂度为 \(O(\frac{n^3}{w}+\sum a\frac{n}{w})\),虽然看起来不可过,但是常数极小.
无力回天
维护 \(m\) 个集合,有集合添加,集合查询并.
我的做法
由于并集涉及元素过多,考虑使用容斥原理转化成交集.
我们发现如果暴力匹配,复杂度与集合大小相关,因此考虑离线,提前记录集合大小,考虑之后的算法.
由于元素一共只有 \(m\) 个,随着每个集合元素个数增加,集合个数减少,故考虑根号分治.
设定集合的大小阈值 \(L\),我们考虑使用两类暴力算法:一种是直接比较,另一种使用 bitset.
- 考虑对于集合大小小于 \(L\) 的元素,使用哈希表存储,添加 \(O(1)\).
- 考虑对于集合大小大于 \(L\) 的元素,使用
bitset存储,添加 \(O(1)\).
考虑查询:
- \(1\to 1\):在哈希表中暴力查找,复杂度为 \(O(L)\).
- \(1\to 2\):在
bitset中暴力查找,复杂度为 \(O(L)\). - \(2\to 2\):暴力合并
bitset,复杂度为 \(O(\frac{m}{w})\).
时间复杂度为 \(O(m(L+\frac{m}{w}))\),空间复杂度为 \(O(m+\frac{m^2}{Lw})\).
上面的太劣了,考虑优化.
我们没有很好利用集合大小大于 \(L\) 的集合个数只有 \(O(\frac{m}{L})\) 个.
考虑直接暴力记录 \(O(\frac{m}{L})\) 个集合之间的贡献,
在每次添加元素时,对另外 \(O(\frac{m}{L})\) 个元素暴力统计,时间复杂度为 \(O(\frac{m}{L})\).
查询时 \(O(1)\).
时间复杂度为 \(O(m(L+\frac{m}{L}))\),空间复杂度为 \(O(m+\frac{m^2}{L^2}+\frac{m^2}{Lw})\).
取 \(L=\sqrt{m}\) 较优,由于常数原因,取 \(L=\frac 12\sqrt{m}\) 更优.
事实上还可以继续优化常数:
我们不使用哈希表存储,使用 vector 存储(由于我们已知集合大小,因此可以提前 reserve,减少常数).
每次插入一个元素,对 vector 进行插入排序,时间复杂度为 \(O(L)\).
每次询问 \(1\to 1\) 时,由于集合时有序的,因此可以使用双指针统计,时间复杂度为 \(O(L)\).
常数大大减小.
洛谷常见做法
对值的出现次数进行根号分治:设定阈值 \(B\).
对于值出现次数大于 \(B\) 的元素,会有 \(O(\frac{m}{B})\) 个,对于每个集合维护一个 bitset 维护这些数,查询 \(O(\frac{m}{Bw})\),修改 \(O(1)\).
对于值出现次数小于 \(B\) 的元素,统计集合之间的贡献,至多会有 \(O(mB)\) 个贡献,使用哈希表存储,查询 \(O(1)\),修改 \(O(B)\).
取 \(b=\sqrt{\frac{m}{w}}\),有最终时间复杂度为 \(O(m\sqrt{\frac{m}{w}})\),空间复杂度为 \(O(m\sqrt{\frac{m}{w}})\).
但是哈希表的常数~
寒蝉鸣泣之时
看到如此简洁的矩阵问题,想到扫描线.
因此我们需要一个数据结构维护:
区间加/减 \(1\),查询 \(O(\sqrt{n})\) 个间隔为 \(m\) 的数的个数.
显然线段树是无法同时维护下标与值域的,因此考虑分块:
不难想到:对于每一个块开一个权值数组,记录权值的个数.
修改时,对于整块,记录值域的偏移量;对于散块,暴力重构,复杂度为 \(O(\sqrt{n})\).
查询时,对于每个区间进行查询,这样复杂度为 \(O(\sqrt{n}\frac{n}{m})\),最坏是 \(O(n)\) 的.
考虑加上如下剪枝:
记录每个块的最大值与最小值,在这个值域区间中查询.
重新计算查询复杂度:
记一个区间的值域长度为 \(L_i\),那么此时单个块查询的数值个数为 \(\frac{L_i}m +O(1)\).
那么总单次查询复杂度为 \(O(\frac{\sum L_i}m+\sqrt{n})\).
现在考虑值域区间的变化:显然,每次整块会使值域整体偏移,不会贡献值域长度;只有散块会使值域区间长度增加,因此 \(O(\sum L_i)=O(n)\),即单次查询复杂度为 \(O(\frac nm+\sqrt{n})\).
综上,时间复杂度为 \(O(n\sqrt{n})\).

浙公网安备 33010602011771号