51Nod1592 数列积

153 篇文章 0 订阅
134 篇文章 0 订阅


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

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

这里面, 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 ,这个东西也可以用一个式子来转移
g[i,j]=g[i,j1]+g[i+1,j1]g[i+1,j2]+|a[i]a[i+j]|j g [ i , j ] = g [ i , j − 1 ] + g [ i + 1 , j − 1 ] − g [ i + 1 , j − 2 ] + | a [ i ] − a [ i + j ] | ∗ j

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

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

对于i后面的元素,只需要将上面式子加上负号就可以了
当我们预处理出 p p 后,自然就得到v[x,y]=i=l[x]r[x]p[i][y]
这样,我们就得到了 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));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值