题目看这里
无比暴力的大分块,极端不优美
听说很多人用莫队水过去了,我还是被最后一个点卡WAing(本地都过了)
说下解法:
首先,将整个序列分块,分成
n−−√
n
块
将区间分成三个部分:前面多出来的+中间若干整块+后面多出来的
价值就可以这样计算:中间若干块+前面部分自身价值+后面部分价值+前面和后面与中间产生的价值+前面和后面产生的价值
分别考虑每一个部分怎么计算
首先我们需要计算每一个块自身的价值,直接暴力,这一部分是
O(n∗n−−√)
O
(
n
∗
n
)
我们记
f[l,r]
f
[
l
,
r
]
表示从第l个块到第r个块的价值,就可以写出dp转移式子:
这里面, v[l,r] v [ l , r ] 表示第l个块和第r个块产生的价值,先不考虑这个怎么计算
再考虑其他的两个东西,我们记 g[i,j] g [ i , j ] 表示以i开头,长度为j+1的一个块的价值,显然 g[i,j]=0 g [ i , j ] = 0 ,这个东西也可以用一个式子来转移
处理完这个东西还不够,还需要预处理一个东西 p[i,j] p [ i , j ] 表示第a[i]与第j块产生的价值
这个东西因为状态数较多,所以需要每次将两块合起来处理,考虑原来的式子就有
这样就可以拆成四个东西来处理,我们在考虑第x块和第y块时(x < < <script type="math/tex" id="MathJax-Element-4097"><</script>y),可以先将x和y排序,让后维护三个前缀和: ∑i∑ai∑i∗ai ∑ i ∑ a i ∑ i ∗ a i (注意,这里已经排序,i无序)
考虑对于块y中的元素j,我们找到x中的分界点i,使得i前面的都小于a[j],那么就有这样的转移:
对于i后面的元素,只需要将上面式子加上负号就可以了
当我们预处理出 p p 后,自然就得到
这样,我们就得到了 p,g,f p , g , f 三个部分
最后说一下前面和后面产生的价值怎么计算,类似计算 p[i,x] p [ i , x ] 的过程,将这一部分排序,让后就是一样的了
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define M 250
#define N 50000
#define I w[i]
#define J w[j]
#define LL unsigned long long
using namespace std;
int n,m,c,q,w[N],l[M],r[M];
LL f[M][M],g[N][M],p[N][M],v[M][M],s[N];
inline bool c1(int i,int j){ return s[i]<s[j]; }
inline LL calB(int l,int r){
LL S=0;
for(int i=l;i<=r;++i)
for(int j=i;j<=r;++j)
S+=(LL)abs((int)s[i]-(int)s[j])*(j-i);
return S;
}
inline LL dp(int l,int r){
if(l>r) return 0;
if(~f[l][r]) return f[l][r];
if(l==r) return f[l][r]=calB(::l[l],::r[r]);
return f[l][r]=dp(l+1,r)+dp(l,r-1)-dp(l+1,r-1)+v[l][r];
}
LL si[N],sa[N],sm[N],S=0;
inline LL gV(int x,int y){
LL S=0;
for(int i=l[x],j=l[y];j<=r[y];++j){
while(i<=r[x] && s[I]<s[J]) ++i;
p[J][x]+=J*s[J]*(i-l[x])+(sm[i-1]-sm[l[x]-1])-J*(sa[i-1]-sa[l[x]-1])-s[J]*(si[i-1]-si[l[x]-1]);
p[J][x]-=J*s[J]*(r[x]-i+1)+(sm[r[x]]-sm[i-1])-J*(sa[r[x]]-sa[i-1])-s[J]*(si[r[x]]-si[i-1]);
S+=p[J][x];
}
for(int i=l[y],j=l[x];j<=r[x];++j){
while(i<=r[y] && s[I]<s[J]) ++i;
p[J][y]-=J*s[J]*(i-l[y])+(sm[i-1]-sm[l[y]-1])-J*(sa[i-1]-sa[l[y]-1])-s[J]*(si[i-1]-si[l[y]-1]);
p[J][y]+=J*s[J]*(r[y]-i+1)+(sm[r[y]]-sm[i-1])-J*(sa[r[y]]-sa[i-1])-s[J]*(si[r[y]]-si[i-1]);
}
return S;
}
inline LL gp(int i,int j){
if(j<=0) return g[i][j]=0;
if(~g[i][j]) return g[i][j];
return g[i][j]=gp(i,j-1)+gp(i+1,j-1)-gp(i+1,j-2)+abs((int)s[i]-(int)s[i+j])*j;
}
inline LL d(int l,int r,int L,int R){
static LL s[N],w[N],si[N],sa[N],sm[N],S;
S=*si=*sm=*sa=0;
for(int i=l;i<=r;++i) s[i]=::s[i],w[i]=i;
for(int i=L;i<=R;++i) s[i]=::s[i],w[i]=i;
sort(w+l,w+r+1,c1); sort(w+L,w+R+1,c1);
for(int i=l;i<=r;++i){
si[i]=si[i-1]+w[i];
sa[i]=sa[i-1]+s[w[i]];
sm[i]=sm[i-1]+w[i]*s[w[i]];
}
for(int i=L;i<=R;++i){
si[i]=si[i-1]+w[i];
sa[i]=sa[i-1]+s[w[i]];
sm[i]=sm[i-1]+w[i]*s[w[i]];
}
for(int i=l,j=L;j<=R;++j){
while(i<=r && s[I]<s[J]) ++i;
S+=J*s[J]*(i-l)+(sm[i-1]-sm[l-1])-J*(sa[i-1]-sa[l-1])-s[J]*(si[i-1]-si[l-1]);
S-=J*s[J]*(r-i+1)+(sm[r]-sm[i-1])-J*(sa[r]-sa[i-1])-s[J]*(si[r]-si[i-1]);
}
return S;
}
inline LL pS(int l,int r,int x,int y){
return p[r][y]-p[l-1][y]-p[r][x-1]+p[l-1][x-1];
}
int main(){
scanf("%d",&n); m=ceil(sqrt(n)); c=(n-1)/m+1;
for(int i=1;i<=n;++i) scanf("%llu",s+i),w[i]=i;
for(int i=1;i<=c;++i) l[i]=r[i-1]+1,r[i]=i*m;
r[c]=n;
for(int i=1;i<=c;++i) sort(w+l[i],w+r[i]+1,c1);
for(int x=1;x<=c;++x)
for(int i=l[x];i<=r[x];++i){
si[i]=si[i-1]+w[i];
sa[i]=sa[i-1]+s[w[i]];
sm[i]=sm[i-1]+w[i]*s[w[i]];
}
for(int i=1;i<=c;++i) for(int j=i+1;j<=c;++j) v[i][j]=gV(i,j);
memset(f,-1,sizeof f); dp(1,m); memset(g,-1,sizeof g);
for(int i=1;i<=n;++i) gp(i,min(m,n-i+1));
for(int i=1;i<=n;++i) for(int j=1;j<=c;++j) p[i][j]+=p[i-1][j]+p[i][j-1]-p[i-1][j-1];
scanf("%d",&q);
for(int L,R;q--;){
scanf("%d%d",&L,&R);
int l1=lower_bound(l+1,l+1+c,L+1)-l-1;
int r1=lower_bound(r+1,r+1+c,R)-r;
if(l1==r1){ printf("%llu\n",g[L][R-L]); }
else if(l1==r1-1) printf("%llu\n",g[L][r[l1]-L]+g[l[r1]][R-l[r1]]+d(L,r[l1],l[r1],R));
else printf("%llu\n",f[l1+1][r1-1]+g[L][r[l1]-L]+g[l[r1]][R-l[r1]]+d(L,r[l1],l[r1],R)+pS(L,r[l1],l1+1,r1-1)+pS(l[r1],R,l1+1,r1-1));
}
}