q次询问,求n条直线在k位置的前k小值之和
只能做到 \(O(n \sqrt n \log n +q)\)。
设 \(n\) 条直线斜率和截距分别为 \(k_i,b_i\),\(f_i(x) = k_i x +b_i\)。
做法:
首先对于所有直线按斜率从小往大分块。
对于序列扫描线,每次我们需要保证的是对于新的 \(x\) 来说,一个块内的 \(f(x)\) 不降。
我们考虑这样一件事,如果我们确定了块中所有对 \((x,y)\) 的相对位置,我们一定可以还原出一个新的序列,初始时每个对都有一个相对位置,由于是两条直线,所以这两个位置的相对位置至多变化一次。
我们可以预处理出每个对变化的时间,再在那个时间啊把所有相对位置可能发生改变的位置取出来,直接对他们排序,再按排序后顺序插回即可。
考察一下这里的复杂度,首先有 \(\frac{n}{B}\) 个块,每个块每个对处理一次,复杂度是 $B^2 \log B $,总复杂度 \(O(n B \log B)\)。
那么显然此时我们的一次选择一定会选择一个块一段前缀,记做 \(cnt_i\)。
假设我们已经得到了 \(k-1\) 位置的前 \(k-1\) 小值和时的所有块的 \(cnt_i\),我们考虑怎么增量到 \(k\)。
我们现在假设当前每个块依旧选择了上一次选择中的前 \(cnt\) 个数,这是需要调整的。
得到每个块前 \(cnt\) 个值的和的方法是,每个块开两个树状数组,维护前缀斜率和截距即可。
我们可以简单的得到一种暴力的调整方法,即每次取出每个块中的第 \(cnt\) 个数的值,再取出每个块 \(cnt+1\) 的值,若前面取出的值存在比未取出的大,就吧前面取出的一个放回,后面的放入,设前面选的组为 \(u\),后面的为 \(v\),此时一定有 \(u \gt v\),因为此时同样相当于一次直线在某个位置交换,显然斜率小的在 \(x\) 增大后会更有优势。
我们断言直接用小根堆存第一个集合,大根堆存第二个,每次做上述操作就是正确的。
考虑证明这个结论,我们定义势能 \(\Phi = \sum_{i=1}^{\frac{n}{B}} i \times cnt_i\)。
每次势能至多增加 \(\frac{n}{B}\),即加入一个最大的块中的值。
每次做上述交换操作一次势能为 \(u - v\),由于 \(u \gt v\),势能至少减少 \(1\)。
由于对于势能正贡献为 \(\frac{n^2}{B}\),且每一次操作都会消耗 \(1\),所以这里复杂度为 \(O(\frac{n^2}{B} \log B)\)。
注意这里的两个堆需要支持删除,可以每个堆再开一个删除堆。
那么总复杂度即为 \((nB+\frac{n^2}{B}) \log B\)。

浙公网安备 33010602011771号