问题描述
给定长度为 $n$ 的序列:$a_1,a_2,…,a_n$,记为 $a[1:n]$。有 $q$ 个询问,每个询问给定两个数 $l,r$,求 $a[l:r]$ 中所有子序列的最小值之和。
输入
第一行包含两个整数 $n$ 和 $q$,分别代表序列长度和询问数。
接下来一行,包含 $n$ 个整数,第 $i$ 个整数为 $a_i$。
接下来 $q$ 行,每行包含两个整数 $l$ 和 $r$,代表一次询问。
输出
对于每次询问,输出一行,代表询问的答案。
思路
有一种比较直观的方法就是使用莫队,队中维护一个单调栈,并且记录栈中元素到前一个元素这段区间的答案。当插入元素时,更新单调栈,通过记录的信息可以比较容易的求出答案。
这题还有一个很妙的 Idea。考虑类似于上面莫队的方法,先处理出右端点为 $i$ 的所有 $a[l,i]$ 的最小值的和,记为 $pre_i$,再处理一个 $pre_i$ 的前缀和 $spre_i$,这样 $spre_i$ 即是 $a[1,i]$ 的答案。类似的再处理出 $suf_i$,表示所有左端点为 $i$ 的序列的最小值之和,以及 $ssuf_i$ 表示 $suf_i$ 的后缀和。
预处理完这些东西后,考虑如何回答区间 $a[l,r]$ 的答案,我们设 $a[l,r]$ 中最小值位于 $pos$,这个可以利用 ST 表求出。经过 $pos$ 这一点的所有区间对答案的贡献可以直接计算出来,考虑剩下 $a[l,pos-1]$ 及 $a[pos+1,r]$ 这两段的贡献如何计算。我们发现 $spre[r]-spre[pos]$ 就包含了 $a[pos+1,r]$ 这一段的答案和右端点在 $[pos+1,r]$ 左端点在 $[1,pos-1]$ 的区间最小值之和。而后面一部分由于跨过了 $pos$,它的贡献可以等价为 $(r-pos)*pre[pos]$,将它们相减就可以计算出 $a[pos+1,r]$ 的贡献,类似的可以同样计算出 $a[l,pos-1]$ 的贡献。
这样我们可以在 $O(1)$ 的时间内回答每一个询问,而算法复杂度的瓶颈在 $O(n \log n)$ 的 ST 表预处理上,而如果使用笛卡尔树或者用一些 $O(n)$ RMQ 的高级姿势,就可以在线性复杂度内完成这个操作。
代码
1 |
|