! HNOI2016序列

\(n1e5\)

求区间,莫队??

确实可以,时间复杂度\(O(nlogn+\sqrt n)\),但DP只用\(O(nlogn)\)

SOL:

\(f[l][r]\)表示右端点为\(r\),左端为\([l,r]\)的答案

\(pre_x\)表示x位置前第一个小于\(a_x\)是位置

\(f[l][r]=f[l][pre_r]+a_r*(r-pre_r)\)

只要我们使得存在\(pre_x=l-1\),那么可以去掉第一维

\[f_{r}=a_r*(r-pre_r)+…+a_x*(x-l+1)+f[l-1] \]

\[f[l][r]=f[r]-f[l-1] \]

我们只需要在\([l,r]\)中找到最小值的位置p,单独算跨过p的区间,就分为两个可以用上述方法解决的子问题(左右各DP一次)

但是这样明显过不去(在此基础上莫队可以卡过),需要优化

用前缀和优化\(s_i=\sum_{j=1}^if_j\)

\[ans=s_r-s_p-f_p*(r-p) \]

时间复杂度\(O(nlogn)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=1e5+4,inf=1e9+7;
int n,m,top,a[N],lg[N],mn[N][20],fl[N],sl[N],fr[N],sr[N],st[N],pre[N],suf[N];
inline int cmp(int x,int y){return a[x]<a[y]?x:y;}
inline int ask(int x,int y){
	int l=lg[y-x+1];
	return cmp(mn[x][l],mn[y-(1<<l)+1][l]);
}
signed main(){
	n=read();m=read();a[0]=inf;a[n+1]=-inf;
	for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
	for(int i=1;i<=n;i++){a[i]=read();mn[i][0]=i;}
	for(int j=1;j<=lg[n];j++)
		for(int i=1,mx=n-(1<<j)+1;i<=mx;i++)//
			mn[i][j]=cmp(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
	for(int i=1;i<=n+1;i++){
		while(top&&a[st[top]]>a[i])suf[st[top--]]=i;
		pre[i]=st[top];st[++top]=i;
	}
	for(int i=1;i<=n;i++){
		fr[i]=a[i]*(i-pre[i])+fr[pre[i]];
		sr[i]=sr[i-1]+fr[i];
	}
	for(int i=n;i;i--){
		fl[i]=a[i]*(suf[i]-i)+fl[suf[i]];
		sl[i]=sl[i+1]+fl[i];
	}
	while(m--){
		static int l,r,p;
		l=read();r=read();p=ask(l,r);
		cout<<(p-l+1)*(r-p+1)*a[p]+
			sr[r]-sr[p]-fr[p]*(r-p)+
			sl[l]-sl[p]-fl[p]*(p-l)<<"\n";
	}
	return (0-0);
}

猜你喜欢

转载自www.cnblogs.com/aurora2004/p/12655380.html