Problem
Solution
刚开始想的时候,觉得要预处理出a[i]做贡献的区域,然后用线段树做。但是这无法解决多组询问。
然后想莫队,发现只会logn的转移。。
然后这就比较尴尬了……
那么我们就重点缩一下如何O(1)转移吧
先讨论r++的情况。
很明显,我们会新增r-l+2个区间,更新答案就需要计算这些区间的贡献
首先我们可以发现在这些区间内,做贡献的值是一个递增的序列。
就比如4 1 3再新增一个2
那么对于新增的四个区间[4,1,3,2],[1,3,2],[3,2][2],做贡献的值分别为1,1,2,2
而且在贡献的值改变时,就是遇到比自己更小的值时。那么我们就可以将这个贡献进行前缀和的预处理,预处理可以用单调栈优化。
但是需要注意的是最左边,也就是最小的数它做贡献的区间是有可能不完整的,需要特别处理一下,设它的位置为pos,那么它的贡献为a[pos]*(pos-l+1)。查询pos用st表解决。
对于l–的情况,与r++所类似,把前缀和改成后缀和即可。
对于另外两个操作是类似的处理方式。
Code
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn=100010;
struct query{
int l,r,pos,id;
bool operator < (const query &x)const
{
if(pos==x.pos) return r<x.r;
return pos<x.pos;
}
}q[maxn];
int n,m,sqr,top,l,r,a[maxn],lg[maxn],st[maxn][17],stk[maxn],pre[maxn],nxt[maxn];
ll tmp,ans[maxn],sum[maxn],suf[maxn];
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
void input()
{
read(n);read(m);
sqr=(int)sqrt(n);
for(int i=1;i<=n;i++){read(a[i]);st[i][0]=i;}
for(int i=1;i<=m;i++)
{
read(q[i].l);read(q[i].r);
q[i].pos=(q[i].l-1)/sqr+1;q[i].id=i;
}
sort(q+1,q+m+1);
}
int query(int l,int r)
{
int k=lg[r-l+1],pos=r-(1<<k)+1;
return a[st[l][k]]<a[st[pos][k]]?st[l][k]:st[pos][k];
}
void update(int op)
{
int pos=query(l,r);
switch(op)
{
case 1:tmp+=(ll)(pos-l+1)*a[pos]+sum[r]-sum[pos];break;
case 2:tmp-=(ll)(pos-l+1)*a[pos]+sum[r]-sum[pos];break;
case 3:tmp-=(ll)(r-pos+1)*a[pos]+suf[l]-suf[pos];break;
case 4:tmp+=(ll)(r-pos+1)*a[pos]+suf[l]-suf[pos];break;
}
}
void init()
{
lg[0]=-1;
for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=16;i++)
for(int j=1;j<=n;j++)
{
int r=j+(1<<(i-1));
if(r<=n) st[j][i]=a[st[j][i-1]]<a[st[r][i-1]]?st[j][i-1]:st[r][i-1];
}
for(int i=1;i<=n;i++)
{
while(top&&a[i]<a[stk[top]]) nxt[stk[top--]]=i;
pre[i]=stk[top];stk[++top]=i;
}
while(top) nxt[stk[top--]]=n+1;
for(int i=1;i<=n;i++) sum[i]=sum[pre[i]]+(ll)(i-pre[i])*a[i];
for(int i=n;i>=1;i--) suf[i]=suf[nxt[i]]+(ll)(nxt[i]-i)*a[i];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
input();
init();
tmp=a[1];l=r=1;
for(int i=1;i<=m;i++)
{
while(r<q[i].r) r++,update(1);
while(l>q[i].l) l--,update(4);
while(r>q[i].r) update(2),r--;
while(l<q[i].l) update(3),l++;
ans[q[i].id]=tmp;
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}