【HNOI2016】序列

来源:互联网 发布:更改路由器mac 编辑:程序博客网 时间:2024/05/21 04:15

题意

给定长度为n的序列:a1,a2,...,anq个询问,每个询问给一个区间,询问该区间的不同子序列的最小值之和。
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时,栈中它底下一个的位置为序列中它左边第一个小于它的位置。此时的栈为1i的一个单调递增序列,此时的贡献也很好统计,我们记为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]的增量也是相同的,只需求个倒序的单调栈即可。

1 0
原创粉丝点击