51nod 1592 数列积 莫队算法+树状数组

题意

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

分析

题解是十分鬼畜的分块做法。明明在询问的时候有排序还说自己的复杂度是 O(nn) 。。。

可以上莫队,直接把式子展开用树状数组维护四项即可。
卡常卡了好久,最后把树状数组的传参减少了,就跑的快了很多。这告诉我们不要传太多参数。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

typedef unsigned long long LL;

const int N=50005;

int n,m,bel[N],a[N],mx,stack[25];
LL c1[N*2],c2[N*2],c3[N*2],c4[N*2],val,ans[N],s1,s2,s3,s4,y;
struct data{int l,r,id;}q[N];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

inline void pri(LL x)
{
    if (!x) {puts("0");return;}
    int top=0;
    while (x) stack[++top]=x%10,x/=10;
    while (top) putchar(stack[top]+'0'),top--;
    puts("");
}

bool cmp(data a,data b)
{
    return bel[a.l]<bel[b.l]||bel[a.l]==bel[b.l]&&a.r<b.r;
}

inline void ins(int x,LL y)
{
    LL w1=x*y,w2=a[x]*y,w3=w1*w2*y;x=a[x];
    while (x<=mx) c1[x]+=y,c2[x]+=w1,c3[x]+=w2,c4[x]+=w3,x+=x&(-x);
}

inline void find(int x)
{
    LL w1=0,w2=0,w3=0,w4=0;
    while (x) w1+=c1[x],w2+=c2[x],w3+=c3[x],w4+=c4[x],x-=x&(-x);
    s1+=w1*y;s2+=w2*y;s3+=w3*y;s4+=w4*y;
}

inline void updata(int x,int op,LL f)
{
    ins(x,f);
    if (op==0)
    {
        s1=s2=s3=s4=0;
        y=1;find(a[x]-1);
        val+=f*((LL)a[x]*s2-(LL)x*a[x]*s1-(LL)s4+(LL)x*s3);
        s1=s2=s3=s4=0;
        y=1;find(mx);y=-1;find(a[x]);
        val+=f*(s4-(LL)x*s3-(LL)a[x]*s2+(LL)x*a[x]*s1);
    }
    else
    {
        s1=s2=s3=s4=0;
        y=1;find(a[x]-1);
        val+=f*((LL)x*a[x]*s1-(LL)a[x]*s2-(LL)x*s3+(LL)s4);
        s1=s2=s3=s4=0;
        y=1;find(mx);y=-1;find(a[x]);
        val+=f*((LL)x*s3-s4-(LL)x*a[x]*s1+(LL)a[x]*s2);
    }
}

void solve()
{
    for (int i=1,l=1,r=0;i<=m;i++)
    {
        for (;r<q[i].r;r++) updata(r+1,1,1);
        for (;l>q[i].l;l--) updata(l-1,0,1);
        for (;r>q[i].r;r--) updata(r,1,-1);
        for (;l<q[i].l;l++) updata(l,0,-1);
        ans[q[i].id]=val;
    }
}

int main()
{
    n=read();int block=sqrt(n);
    for (int i=1;i<=n;i++) a[i]=read(),bel[i]=(i+block-1)/block,mx=max(mx,a[i]);
    m=read();
    for (int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
    sort(q+1,q+m+1,cmp);
    solve();
    for (int i=1;i<=m;i++) pri(ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值