51Nod1592 数列积


题目看这里
无比暴力的大分块,极端不优美
听说很多人用莫队水过去了,我还是被最后一个点卡WAing(本地都过了)
说下解法:
首先,将整个序列分块,分成n
将区间分成三个部分:前面多出来的+中间若干整块+后面多出来的
价值就可以这样计算:中间若干块+前面部分自身价值+后面部分价值+前面和后面与中间产生的价值+前面和后面产生的价值
分别考虑每一个部分怎么计算
首先我们需要计算每一个块自身的价值,直接暴力,这一部分是O(nn)
我们记f[l,r]表示从第l个块到第r个块的价值,就可以写出dp转移式子:

f[l,r]=f[l+1,r]+f[l,r1]f[l+1,r1]+v[l,r]

这里面,v[l,r]表示第l个块和第r个块产生的价值,先不考虑这个怎么计算
再考虑其他的两个东西,我们记g[i,j]表示以i开头,长度为j+1的一个块的价值,显然g[i,j]=0,这个东西也可以用一个式子来转移
g[i,j]=g[i,j1]+g[i+1,j1]g[i+1,j2]+|a[i]a[i+j]|j

处理完这个东西还不够,还需要预处理一个东西p[i,j]表示第a[i]与第j块产生的价值
这个东西因为状态数较多,所以需要每次将两块合起来处理,考虑原来的式子就有
(aiaj)(ji)=aij+ajiaiiajj

这样就可以拆成四个东西来处理,我们在考虑第x块和第y块时(x<y),可以先将x和y排序,让后维护三个前缀和:iaiiai(注意,这里已经排序,i无序)
考虑对于块y中的元素j,我们找到x中的分界点i,使得i前面的都小于a[j],那么就有这样的转移:
p[j,x]=jaji+iaijaia[j]i

对于i后面的元素,只需要将上面式子加上负号就可以了
当我们预处理出p后,自然就得到v[x,y]=i=l[x]r[x]p[i][y]
这样,我们就得到了p,g,f三个部分
最后说一下前面和后面产生的价值怎么计算,类似计算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));
    }
}
posted @ 2018-08-01 09:05  扩展的灰(Extended_Ash)  阅读(178)  评论(0编辑  收藏  举报