洛谷P5501 来者不拒,去者不追(莫队+树状数组+分块)

神仙毒瘤卡常题

首先$O(n\sqrt{n}logn)$的想法不难想到:跑莫队,每次加入一个$a_i$的贡献是$\text{比它小的数个数+1}\times a_i+\text{比它大的数之和}$,删除同理,在值域上跑树状数组即可。

然后你被卡成了暴力

让我们想一下:一个莫队题,插入/删除都是$O(logn)$的,希望能有一个$O(n\sqrt{n})$的算法。

二次离线莫队!

然后你发现$O(m\sqrt{n})$个询问连存都存不下。。。不过学过二次离线莫队的肯定知道一个把询问减少到$O(m)$的方法:利用差分,有一部分可以预先跑出来的就直接算掉。

以右端点右移为例,记区间$[l,r]$内比$x$小的数为$cnt_{l,r,x}$,那么每次插入时比$a_{y+1}$小的数个数是$cnt_{1,y,a_{y+1}}-cnt_{1,x-1,a_{y+1}}$,注意到$cnt_{1,y,a_{y+1}}$是固定的,而且访问是连续的,我们可以记一个前缀和,就可以在模拟莫队时直接算好,而$cnt_{1,x-1,a_{y+1}}$是固定的,我们只要存下$query_{x-1,y+1,y'}$就能囊括这些信息,其他情况同理。

那么,离线完成了,该怎么求呢?树状数组

我们需要一个插入$O(\sqrt{n})$,查询$O(1)$的数据结构,不难想到值域分块,在块内维护前缀和(计算更大的数之和时应维护后缀和),对$\sqrt{100000}$个整块再维护一个前缀和,每次修改最多涉及$\sqrt{100000}$个块内前缀和与$\sqrt{100000}$个整块前缀和,查询时则直接把整块前缀和与块内前缀和相加就得到了答案。

接下来要做的就是卡常了

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define rg register
typedef long long ll;
const int N=500050;
char rB[1<<23],*rS,*rT,wB[(1<<23)+50];
int wp=-1;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<23,stdin),rS==rT)?EOF:*rS++;}
inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;}
inline int rd(){
    rg char c=gc();
    while(c<48||c>57)c=gc();
    rg int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
short buf[25];
inline void wt(ll x){
    if(wp>(1<<23))flush();
    rg short l=-1;
    while(x>9){
        buf[++l]=x%10;
        x/=10;
    }
    wB[++wp]=x|48;
    while(l>=0)wB[++wp]=buf[l--]|48;
    wB[++wp]='\n';
}
int a[N],l[N],r[N],r1[N],sz,qszl=0,qpl[N],qxl[N],qyl[N],qscl[N],qszr=0,qpr[N],qxr[N],qyr[N],qscr[N],rl[N],rr[N],idl[N],idr[N];
short dl[N],dr[N];
ll s[N],resl[N],resr[N],qssl[N],qssr[N];
namespace BIT{  //树状数组(用于预处理)
    int c[N];
    ll C[N];
    inline void add(int x){
        rg int p=x;
        for(;p<=100000;p+=p&-p)++c[p];
        for(p=x;p;p^=p&-p)C[p]+=x;
    }
    inline int rnk(int x){
        rg int s=0;
        for(;x;x^=x&-x)s+=c[x];
        return s;
    }
    inline ll sum(int x){
        rg ll s=0ll;
        for(;x<=100000;x+=x&-x)s+=C[x];
        return s;
    }
}
namespace kuai{  //值域分块
    short sz,cnt,id[100050];
    int c[100050],ck[255],bl[255],br[255];
    ll s[100050],sk[255];
    inline void build(){
        rg int j=1;
        sz=400;cnt=250;
        for(rg short i=1;i<=250;++i){
            for(bl[i]=j;j<=i*sz;++j)id[j]=i;
            br[i]=j-1;
        }
    }
    inline void add(int x){
        rg short p=id[x];
        rg int i;
        for(i=x;i<=br[p];++i)++c[i];
        for(i=x;i>=bl[p];--i)s[i]+=x;
        for(i=p;i<=cnt;++i)ck[i]=ck[i-1]+c[br[i]];
        for(i=p;i;--i)sk[i]=sk[i+1]+s[bl[i]];
    }
    inline int rnk(int x){return !x?0:ck[id[x]-1]+c[x];}
    inline ll sum(int x){return x>100000?0ll:sk[id[x]+1]+s[x];}
}
inline bool cmp1(int x,int y){return l[x]/sz==l[y]/sz?r[x]<r[y]:l[x]<l[y];}
inline bool cmpl(int x,int y){return qpl[x]<qpl[y];}
inline bool cmpr(int x,int y){return qpr[x]>qpr[y];}
int main(){
    rg int n=rd(),q=rd(),i,j,k,x=1,y=0;
    rg ll tmp;
    sz=sqrt(n)*0.9;
    for(i=1;i<=n;++i)a[i]=rd();
    kuai::build();
    for(i=1;i<=n;++i){
        resl[i]=resl[i-1]+(ll)(BIT::rnk(a[i]-1)+1)*a[i]+BIT::sum(a[i]+1);
        BIT::add(a[i]);
    }
    memset(BIT::c,0,sizeof(BIT::c));memset(BIT::C,0,sizeof(BIT::C));
    for(i=n;i;--i){
        resr[i]=resr[i+1]+(ll)(BIT::rnk(a[i]-1)+1)*a[i]+BIT::sum(a[i]+1);
        BIT::add(a[i]);
    }
    for(i=0;i<q;++i){l[i]=rd();r[i]=rd();r1[i]=i;}
    sort(r1,r1+q,cmp1);
    for(i=0;i<q;++i){
        s[r1[i]]=resl[r[r1[i]]]-resl[y]+resr[l[r1[i]]]-resr[x];
        if(r[r1[i]]>y){qpl[++qszl]=x-1;qxl[qszl]=y+1;qyl[qszl]=r[r1[i]];rl[qszl]=qszl;idl[qszl]=r1[i];dl[qszl]=-1;}
        if(y>r[r1[i]]){qpl[++qszl]=x-1;qxl[qszl]=r[r1[i]]+1;qyl[qszl]=y;rl[qszl]=qszl;idl[qszl]=r1[i];dl[qszl]=1;}
        y=r[r1[i]];
        if(l[r1[i]]<x){qpr[++qszr]=y+1;qxr[qszr]=l[r1[i]];qyr[qszr]=x-1;rr[qszr]=qszr;idr[qszr]=r1[i];dr[qszr]=-1;}
        if(x<l[r1[i]]){qpr[++qszr]=y+1;qxr[qszr]=x;qyr[qszr]=l[r1[i]]-1;rr[qszr]=qszr;idr[qszr]=r1[i];dr[qszr]=1;}
        x=l[r1[i]];
    }
    sort(rl+1,rl+qszl+1,cmpl);sort(rr+1,rr+qszr+1,cmpr);
    for(i=1,j=0;i<=qszl;++i){
        while(j<qpl[rl[i]])kuai::add(a[++j]);
        for(tmp=0ll,k=qxl[rl[i]];k<=qyl[rl[i]];++k)tmp+=(ll)kuai::rnk(a[k]-1)*a[k]+kuai::sum(a[k]+1);
        s[idl[rl[i]]]+=tmp*dl[rl[i]];
    }
    memset(kuai::c,0,sizeof(kuai::c));memset(kuai::s,0,sizeof(kuai::s));memset(kuai::ck,0,sizeof(kuai::ck));memset(kuai::sk,0,sizeof(kuai::sk));
    for(i=1,j=n+1;i<=qszr;++i){
        while(j>qpr[rr[i]])kuai::add(a[--j]);
        for(tmp=0ll,k=qxr[rr[i]];k<=qyr[rr[i]];++k)tmp+=(ll)kuai::rnk(a[k]-1)*a[k]+kuai::sum(a[k]+1);
        s[idr[rr[i]]]+=tmp*dr[rr[i]];
    }
    for(i=1;i<q;++i)s[r1[i]]+=s[r1[i-1]];
    for(i=0;i<q;++i)wt(s[i]);
    flush();
    return 0;
}
View Code

 

posted @ 2019-08-12 15:17  wangyuchen  阅读(187)  评论(0编辑  收藏  举报