[BZOJ4540][HNOI2016]序列
sol
区间?可离线?顺手就掏出了莫队。
最主要的问题就是移动区间怎么做到\(O(1)\)更新答案。
我们考虑由\([L+1,R]\)移动到\([L,R]\),
这样一共新产生了\(R-L+1\)个子区间\([L,L],[L,L+1]...[L,R]\),
设\([L,R]\)中最小值的位置为\(P\),那么上述区间中\([L,P]...[L,R]\)的贡献就全都是\(a[P]\)。
考虑用\(sr[i]\)表示左端点是\(i\),右端点在\(i\)到\(n\)之间的所有区间的最小值之和。
那么上述中剩余的区间\([L,L]...[L,P-1]\)的贡献就应为\(sr[L]-sr[P]\)。
处理\(sr\)数组:\(sr[i]=sr[nxt[i]]+(nxt[i]-i)*a[i]\)其中\(nxt[i]\)表示\(i\)之后第一个比\(a[i]\)小的数的位置。
同理处理左边的\(sl\)数组。
复杂度\(O(n\sqrt{n}+m\sqrt{n})\)
加一个莫队的注意事项:关于
while(R<q[i].r)
、while(L>q[i].l)
、while(R>q[i].r)
以及while(L<q[i].l)
的顺序关系。
最优的顺序就是像上面一样:右端点扩张->左端点扩张->右端点收缩->左端点收缩。不正确的处理顺序会导致左端点的位置在右端点的右边这种从而RE。
code
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e5+5;
int n,m,block,a[N],lg[N],ST[20][N],lst[N],nxt[N],Stack[N];
ll sl[N],sr[N],Ans,ans[N];
struct query{
int l,r,id;
bool operator < (const query b) const
{return l/block<b.l/block||(l/block==b.l/block&&r<b.r);}
}q[N];
int Query(int l,int r)
{
int k=lg[r-l+1];
return a[ST[k][l]]<=a[ST[k][r-(1<<k)+1]]?ST[k][l]:ST[k][r-(1<<k)+1];
}
int main()
{
n=gi();block=sqrt(n);m=gi();lg[0]=-1;
for (int i=1;i<=n;++i) a[i]=gi(),ST[0][i]=i,lg[i]=lg[i>>1]+1;
for (int j=1;j<20;++j)
for (int i=1;i+(1<<j-1)<=n;++i)
ST[j][i]=a[ST[j-1][i]]<=a[ST[j-1][i+(1<<j-1)]]?ST[j-1][i]:ST[j-1][i+(1<<j-1)];
for (int i=1,top=0;i<=n;++i)
{
while (top&&a[Stack[top]]>=a[i]) --top;
lst[i]=Stack[top];sl[i]=sl[lst[i]]+1ll*(i-lst[i])*a[i];
Stack[++top]=i;
}
Stack[0]=n+1;
for (int i=n,top=0;i;--i)
{
while (top&&a[Stack[top]]>=a[i]) --top;
nxt[i]=Stack[top];sr[i]=sr[nxt[i]]+1ll*(nxt[i]-i)*a[i];
Stack[++top]=i;
}
for (int i=1;i<=m;++i) q[i]=(query){gi(),gi(),i};
sort(q+1,q+m+1);
int L=1,R=0;
for (int i=1,pos;i<=m;++i)
{
while (R<q[i].r)
{
++R;pos=Query(L,R);
Ans+=1ll*(pos-L+1)*a[pos]+sl[R]-sl[pos];
}
while (L>q[i].l)
{
--L;pos=Query(L,R);
Ans+=1ll*(R-pos+1)*a[pos]+sr[L]-sr[pos];
}
while (R>q[i].r)
{
pos=Query(L,R);
Ans-=1ll*(pos-L+1)*a[pos]+sl[R]-sl[pos];--R;
}
while (L<q[i].l)
{
pos=Query(L,R);
Ans-=1ll*(R-pos+1)*a[pos]+sr[L]-sr[pos];++L;
}
ans[q[i].id]=Ans;
}
for (int i=1;i<=m;++i) printf("%lld\n",ans[i]);
return 0;
}