[BZOJ4540][HNOI2016]序列

BZOJ
Luogu

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;
}
posted @ 2018-02-21 22:18  租酥雨  阅读(167)  评论(0编辑  收藏  举报