洛谷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; }