【51nod1592】数列积

Description

小明有一个含有n个数的数列 a1,a2,,an a 1 , a 2 , ⋯ , a n
他定义一个数列的积为 ni=1nj=i|aiaj|(ji) ∑ i = 1 n ∑ j = i n | a i − a j | ∗ ( j − i )
他发现算出数列积实际上非常简单。因此他现在有了一个绝妙的主意。
他有Q个询问。
对于每个询问会给定两个参数 l,r l , r
他想知道的是,将 al,al+1,,ar a l , a l + 1 , ⋯ , a r 拿出来成为一个数列,问该数列的数列积是多少。
由于答案很大,他表示只要求出数列积对 264 2 64 取模后的值就可以了。

Solution

没有修改,考虑区间扩大缩小答案的增减量。
先讲讲0分做法,我们发现式子贡献可以拆开,维护四个值:个数, i i 的和,ai的和, aii a i ⋅ i 的和。于是有经典莫队算法,时间复杂度可以达到 O(nnlog2n) O ( n n l o g 2 n ) 级别。此时如果运气好就可以过了。
将一个理论更优的做法:把序列分块,然后每一块的贡献可以暴力算。
考虑块与块之间的贡献,我们对于每一块维护它的排序块,枚举后面的块的排序块,用指针扫一下前面的排序块,算一下贡献即可。
然后就可以算第 l l 块到第r块的贡献。
对于一个询问,它包含中间的完整块部分,还有左右的多余部分。
考虑多余部分怎么计算,先计算左(右)边自己的贡献:可以设一个 fi,j f i , j 表示从 i i i+j的贡献,可以预处理;然后计算数对分别在左右多余部分的贡献:把多余部分标记,然后枚举右边多余部分所在的排序块,左边指针扫,遇到标记就加入即可。
还有左右多余元素与中间块的贡献:把所有元素排序,枚举每个块,然后计算一个元素与一个块的贡献,计算方法类似前面,但要判断一下元素与块的前后关系。
有了一个元素与一个块的贡献,就可以计算一个元素与前缀块贡献,于是可以计算一个元素与一段连续块的贡献。
结合上面的其他预处理,可以获得10分。
其实上面的预处理需要开很多 nn n n 的数组,合理寻址优化是通过这题的关键。
结合上面的算法,或许可以获得满分。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef unsigned long long ll;
const int N=5e4+10,_N=250;
int be[N];
struct node{
    int l,r;
}c[_N];
ll a[N];
struct node2{
    ll x;
    int p;
}b[N],d[N];
ll F[_N][_N],vl[_N][_N];
ll f[_N][N],pre[_N][N];
ll sum[N][_N];
int bz[N];
int read(){
    char ch=' ';int t=0;
    for(;ch<'0' || ch>'9';ch=getchar());
    for(;ch>='0' && ch<='9';ch=getchar()) t=(t<<1)+(t<<3)+ch-48;
    return t;
}
ll readll(){
    char ch=' ';ll t=0;
    for(;ch<'0' || ch>'9';ch=getchar());
    for(;ch>='0' && ch<='9';ch=getchar()) t=(t<<1)+(t<<3)+ch-48;
    return t;
}
bool cmp(node2 x,node2 y){
    return x.x<y.x;
}
int abs(int x){
    return x<0?-x:x;
}
int main()
{
    int n=read(),_n;
    for(_n=1;_n*_n<=n;_n++);
    int t=0,tt=1;
    c[1].l=1;
    fo(i,1,n){
        be[i]=tt;
        a[i]=readll(),b[i].x=a[i],b[i].p=i;
        d[i]=b[i];
        if((++t)==_n) c[tt].r=i,c[++tt].l=i+1,t=0;
    }
    if(c[tt].l<=n) c[tt].r=n;
    else c[tt--].l=0;
    fo(i,1,tt){
        int l=c[i].l,r=c[i].r;
        fo(j,l,r-1) fo(k,j+1,r) F[i][i]+=(ll)abs((int)a[k]-(int)a[j])*(k-j);
        sort(b+l,b+r+1,cmp);
        fo(j,1,i-1){
            ll s1=0,s2=0,s3=0;//s1:i s2:a[i] s3:a[i]*i
            int p=c[j].l-1;
            fo(k,l,r){
                int u=b[k].p;
                while(p<c[j].r && b[p+1].x<=a[u]) p++,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x;
                vl[j][i]+=a[u]*u*(ll)(p-c[j].l+1)+s3-a[u]*s1-s2*u;
            }
            s1=s2=s3=0,p=c[j].r+1;
            fd(k,r,l){
                int u=b[k].p;
                while(p>c[j].l && b[p-1].x>a[u]) p--,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x;
                vl[j][i]+=a[u]*s1+s2*u-s3-a[u]*u*(ll)(c[j].r-p+1);
            }
        }
    }
    fo(l,2,tt)
    fo(i,1,tt-l+1){
        int j=i+l-1;
        F[i][j]=F[i][j-1]+F[i+1][j]-F[i+1][j-1]+vl[i][j];
    }
    fo(i,1,n-1) f[1][i]=(ll)abs((int)a[i]-(int)a[i+1]);
    fo(j,2,_n)
    fo(i,1,n-j) f[j][i]=f[j-1][i]+f[j-1][i+1]-f[j-2][i+1]+(ll)abs((int)a[i]-(int)a[i+j])*j;
    sort(d+1,d+n+1,cmp);
    fo(i,1,tt){
        int p=c[i].l-1;
        ll s1=0,s2=0,s3=0;
        fo(j,1,n){
            int k=d[j].p;
            if(be[k]==i) continue;
            while(p<c[i].r && b[p+1].x<=a[k]) p++,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x;
            pre[i][j]=(a[k]*k*(p-c[i].l+1)+s3-a[k]*s1-k*s2)*(p<k?1:-1);
        }
        s1=s2=s3=0,p=c[i].r+1;
        fd(j,n,1){
            int k=d[j].p;
            if(be[k]==i) continue;
            while(p>c[i].l && b[p-1].x>a[k]) p--,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x;
            pre[i][j]+=(a[k]*s1+k*s2-s3-a[k]*k*(c[i].r-p+1))*(p<k?1:-1);
        }
    }
    fo(i,1,n)
    fo(j,1,tt) sum[d[i].p][j]=sum[d[i].p][j-1]+pre[j][i];
    int q;
    q=read();
    fo(z,1,q){
        int l=read(),r=read();
        int L=be[l],R=be[r];
        int l1=c[L].l,r1=c[L].r,l2=c[R].l,r2=c[R].r;
        if(L==R){
            printf("%llu\n",f[r-l][l]);
            continue;
        }
        ll ans=0;
        ans+=f[r1-l][l]+f[r-l2][l2];
        if(L+1<R){
            ans+=F[L+1][R-1];
            fo(i,l,c[L].r) ans+=sum[i][R-1]-sum[i][L];
            fo(i,c[R].l,r) ans+=sum[i][R-1]-sum[i][L];
        }
        fo(i,l,r1) bz[i]=z;
        fo(i,l2,r) bz[i]=z;
        int p=l1-1;
        ll s1=0,s2=0,s3=0,t=0;
        fo(i,l2,r2){
            int k=b[i].p;
            if(bz[k]!=z) continue;
            while(p<r1 && b[p+1].x<=a[k])
            if(bz[b[++p].p]==z) t++,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x;
            ans+=(ll)a[k]*k*t+s3-a[k]*s1-k*s2;
        }
        t=s1=s2=s3=0,p=r1+1;
        fd(i,r2,l2){
            int k=b[i].p;
            if(bz[k]!=z) continue;
            while(p>l1 && b[p-1].x>a[k])
            if(bz[b[--p].p]==z) t++,s1+=b[p].p,s2+=b[p].x,s3+=b[p].p*b[p].x;
            ans+=a[k]*s1+k*s2-s3-a[k]*k*t;
        }
        printf("%llu\n",ans);
    }
}
posted @ 2018-06-03 21:24  sadstone  阅读(36)  评论(0编辑  收藏  举报