P3246 [HNOI2016]序列

题目大意:

给出一个长为n的序列a,m次查询,对于每次查询给定一个区间[l,r],求出该区间所有子序列中最小值的和。

题目解法:

  • 离线算法(莫队)

  显然,对于这种没有修改的查询我们可以考虑莫队。难点在于怎么从[l,r]转移到[l,r+1](另外几种转移同理)。我们发现这样的转移对答案带来贡献的即是左端点在[l,r+1]右端点为r+1的所有区间最小值的和。假如我们能将这个值O(1)算出那么复杂度就没有问题了。所以考虑预处理。fr[l][r]表示左端点在[l,r]右端点在r的所有区间最小值的和。

  我们想到静态找最小值常用的st算法。经过O(n log n)的预处理后,对于每个区间,我们都可以O(1)求出该区间内的最小值。设[l,r]的最小值在p这个位置,那么显然,当左端点在[l,p]时最小值均为a[p]。我们只需要再计算fr[p+1][r]即可。我们注意到fr数组显然不可能是二维的,那么我们必须要对其进行压缩。我们将第一维去掉得到fr'[r]表示左端点在[1,r],右端点在r的所有区间的最小值和。我们注意到如果将fr'[r]减去左端点在[l,p]右端点在r的所有区间最小值和即可得到fr[p+1][r]。我们进一步注意到因为p位置是该区间内最小值,因此[p+1,r]的数应该均不小于a[p]即 左端点在[l,p]右端点在r的所有区间最小值和 等价于 左端点在[l,p]右端点在p的所有区间最小值和,即fr'[p]。

  这样,只要我们将fr'[r]这个数组预处理出来,每次修改O(1)更新即可实现。再引入一个数组lst[i]表示上一个比i小的数(可以用栈、bit等方法求得),那么fr'[r]=fr'[lst[r]]+a[r]*(r-lst[r])。

  对称地,我们还需要一个fl数组记录左端点固定的情况,用于处理l变化的转移。

  复杂度O(n log n + n\sqrt{n})

  • 在线算法

  还是从st算法求得的区间最小位置入手。对于一个区间[l,r],若最小的位置为p,那么所有左端点在[l,p]右端点在[p,r]的区间最小值都应该为a[p]。剩下的只有区间全部位于p的一侧的点。

  讨论左侧,右侧对称。

  左侧的答案即 \sum_{i=p+1}^{r} fr[p+1][i]=\sum_{i=l}^{p-1}fr'[i]-fr'[p]=\sum_{i=l}^{p-1}fr'[i] - (r-p)×fr'[p],处理一下前缀和即可O(1)算出。

  复杂度O(n log n)

猜你喜欢

转载自www.cnblogs.com/myrcella/p/11710111.html