【HNOI2016】序列

题意

给定长度为 n 的序列: a1,a2,...,an q 个询问,每个询问给一个区间,询问该区间的不同子序列的最小值之和。
n,q105

解法

     Philips Weng大神(%%%)用一个线段树存8个值的 nlog2n 做法过了,但我这里介绍一个 nn 的莫队算法(T3也是莫队,D1T1也是分块,这是有多喜欢 n ?)。
     我们假设当前求出了区间 [l,r] 的答案,现在要求 [l,r+1] 相对于 [l,r] 增加的答案( [l,r1] 的答案可以用原答案减去 [l,r] 相对于 [l,r1] 增加的答案)。
     新增的答案为 r+1i=lmin(i,r+1) min(i,r+1) 表示区间 [i,r+1] 中的最小值。有一个暴力的想法,维护一个从左至右单调递增的栈,然后扫一遍栈,算出贡献。但是每次构一次栈显然是不能接受的。
     于是我们可以预处理出一个从 1 开始的栈,当做到一个位置 i 时,栈中它底下一个的位置为序列中它左边第一个小于它的位置。此时的栈为 1 i 的一个单调递增序列,此时的贡献也很好统计,我们记为 sum[i] (因为总贡献为相邻元素的贡献之和,所以它相当于栈中元素的贡献前缀和)。
     回到刚刚的问题,我们可以找到 [l,r+1] 中最小值的位置 pos ,对于 i[l,pos] ,它们对增量的贡献为 a[pos] ;而对于 i[pos+1,r+1] ,我们相当于要跟原来一样构一个单调栈算一下贡献,但是我们发现,这个单调栈是我们预处理出的 到 r+1 的前缀单调栈的, 开头为 pos 的“后缀”。这个是显然的,因为 a[pos] 是该区间的最小值,所以 pos 一定会出现在到 r+1 的前缀单调栈中。所以这部分的贡献 =sum[r+1]sum[pos] 。总贡献为 (posl+1)a[pos]+sum[r+1]sum[pos] 。我们只要知道区间最小值的位置就能 O(1) 求出答案增量了,而这个就是经典的 RMQ 了。
     对于求 [l1,r] 的增量也是相同的,只需求个倒序的单调栈即可。

你可能感兴趣的:(莫队)