传送门
题解:
这道题的线段树做法好神啊。。
考虑直接从 1 1 扫描到 n n ,不断更新 1~i 1 ~ i 到 i i 的最小值。
那么显然我们要对线段树支持区间覆盖, 区间求历史和。
然后我们可以构造矩阵来完成,对于每个节点构造
查询直接求和即可。
#include
typedef long long LL;
using namespace std;
inline int rd() {
char ch=getchar(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
const int N=1e5+50;
int n,q,a[N],p[N],st[N],top;
vector < pair<int,int> >qry[N];
struct Tag {
LL a,b,c,d;
Tag(LL a=0,LL b=0,LL c=1,LL d=0):a(a),b(b),c(c),d(d){}
friend inline Tag operator *(const Tag &a,const Tag &b) {
return Tag(b.a+a.a*b.c, b.b+a.a*b.d+a.b, a.c*b.c, a.c*b.d+a.d);
}
}tag[N*4];
LL s1[N*4],s2[N*4],ans[N];
inline void modify(int k,int l,int r,Tag t) {
s1[k]=s1[k]+t.a*s2[k]+t.b*(r-l+1);
s2[k]=t.c*s2[k]+t.d*(r-l+1);
tag[k]=t*tag[k];
}
inline void pushdown(int k,int l,int r) {
int mid=(l+r)>>1;
modify(k<<1,l,mid,tag[k]);
modify(k<<1|1,mid+1,r,tag[k]);
tag[k]=Tag(0,0,1,0);
}
inline void upt(int k) {
s1[k]=s1[k<<1]+s1[k<<1|1];
s2[k]=s2[k<<1]+s2[k<<1|1];
}
inline void modify(int k,int l,int r,int L,int R,Tag t) {
if(L<=l&&r<=R) {modify(k,l,r,t); return;}
pushdown(k,l,r);
int mid=(l+r)>>1;
if(R<=mid) modify(k<<1,l,mid,L,R,t);
else if(L>mid) modify(k<<1|1,mid+1,r,L,R,t);
else modify(k<<1,l,mid,L,R,t),modify(k<<1|1,mid+1,r,L,R,t);
upt(k);
}
inline LL query(int k,int l,int r,int L,int R) {
if(L<=l&&r<=R) {return s1[k];}
pushdown(k,l,r);
int mid=(l+r)>>1;
if(R<=mid) return query(k<<1,l,mid,L,R);
else if(L>mid) return query(k<<1|1,mid+1,r,L,R);
else return query(k<<1,l,mid,L,R)+query(k<<1|1,mid+1,r,L,R);
}
int main() {
n=rd(), q=rd();
for(int i=1;i<=n;i++) a[i]=rd();
for(int i=1;i<=q;i++) {
int x=rd(), y=rd();
qry[y].push_back(make_pair(x,i));
}
for(int i=1;i<=n;i++) {
while(top&&st[top]>=a[i]) --top;
modify(1,1,n,p[top]+1,i,Tag(0,0,0,a[i]));
st[++top]=a[i]; p[top]=i;
modify(1,1,n,1,i,Tag(1,0,1,0));
for(int j=qry[i].size()-1;~j;j--) {
int v=qry[i][j].first;
ans[qry[i][j].second]=query(1,1,n,v,i);
}
}
for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
}