题目链接
求 l,r 所有子集最小值和
分析
又是区间查询,上莫队了.首先想[l,r]-> [l,r+1] 它的增量是多少.
设[l,r+1] 的最小值的位置是
p
, 那么显然增加的贡献为
设 sl[i] 表示以 i 为右端点的所有区间的贡献前缀和,那么显然当前区间中
r+1 为右端点的贡献为 sl[r+1]−sl[p]
记
l[i] : 左边第一个比a[i] 严格小的位置
r[i] : 右边第一个小于等于a[i] 的位置
那么显然
sl[i]=sl[l[i]]+a[i]∗(i−l[i])
因此用单调栈预处理 l[i],r[i] ,然后用ST表查询最小值的位置就好了
中途WA了很多次,原来莫队更新的顺序很重要,特别是与区间相关,点不独立的问题
AC code
#include <bits/stdc++.h>
using namespace std;
#define ms(x,v) (memset((x),(v),sizeof(x)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int > Pair;
const int maxn = 1e5+10;
int n,m;
LL a[maxn];
const int S = 300;
struct Query{
int l,r,id;
bool operator < (const Query & o)const{
return l/S == o.l/S?r < o.r:l<o.l;
}
};
Query q[maxn];
int l[maxn],r[maxn],stk[maxn],top;
LL sl[maxn],sr[maxn],ret[maxn];
void init() {
top=0;
for(int i=1 ; i<=n ; ++i){
while (top && a[stk[top]] >=a[i])r[stk[top--]] =i;//被右端第一个小于等于它的元素驱逐出栈
stk[++top] =i;
}
top=0;
for(int i=n ; i>0 ; --i){
while (top && a[stk[top]] > a[i])l[stk[top--]] = i;
stk[++top] = i;
}
sl[0] = 0;sr[n+1] = 0;l[1] =0;r[n] = n+1;
for(int i=1 ; i<=n ; ++i)sl[i] = sl[l[i]]+(LL)a[i]*(i-l[i]);
for(int i=n ; i>0 ; --i)sr[i] = sr[r[i]] + (LL)a[i]*(r[i]-i);
}
namespace ST{
int st[maxn][22],lg2[maxn];
void init_rmq(int n,LL a[]) {
lg2[0] =-1;
for(int i=1 ; i<=n ; ++i){
lg2[i] = ((i&(i-1)) == 0)?lg2[i-1]+1:lg2[i-1];
st[i][0] =i;
}
for(int j=1 ; j<=lg2[n]; ++j)
for(int i=1 ; i+(1<<j)-1 <=n ; ++i)
st[i][j] = (a[st[i][j-1]]<a[st[i+(1<<(j-1))][j-1]])?st[i][j-1]:st[i+(1<<(j-1))][j-1];
}
int rmq(int l,int r,LL a[]){
int k = lg2[r-l+1];
return a[st[l][k]] < a[st[r-(1<<k)+1][k]]?st[l][k]:st[r-(1<<k)+1][k];
}
};
using namespace ST;
inline LL moveL(int l,int r) {
int p = rmq(l,r,a);
return sr[l]-sr[p]+a[p]*(r-p+1);
}
inline LL moveR(int l,int r){
int p = rmq(l,r,a);
return sl[r] - sl[p]+a[p]*(p-l+1);
}
int main(){
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1 ; i<=n ; ++i)cin>>a[i];
init_rmq(n,a);
init();
for(int i=0 ; i<m ; ++i){
q[i].id = i;cin>>q[i].l >> q[i].r;
}
sort(q,q+m);
int curL=q[0].l,curR = q[0].l-1;
LL ans =0;
for(int i=0 ; i<m ; ++i){
int L = q[i].l,R = q[i].r;
while (curR < R)ans += moveR(curL,++curR);
while (curL > L)ans += moveL(--curL,curR);
while (curR > R)ans -= moveR(curL,curR--);
while (curL < L)ans -=moveL(curL++,curR);
ret[q[i].id] = ans;
}
for(int i=0 ; i<m ; ++i)std::cout << ret[i] << '\n';
return 0;
}