【洛谷P5501】[LnOI2019]来者不拒,去者不追
二次离线莫队
题目大意:
给定一个数列,每次询问区间 $[l,r]$。
对于区间里的每个数 $a_i$,如果区间里有 $k$ 个数比它小,则这个数的贡献就是 $(k+1)a_i$。
求区间里所有数的贡献之和。
题解:
二次离线莫队。
考虑莫队时,新加一个数会产生多少新的贡献。
设 $f(a,l,r)$ 表示区间 $[l,r]$ 中比 $a$ 小的数的个数,$g(a,l,r)$ 表示区间 $[l,r]$ 中比 $a$ 大的数的和。
那么 $[l,r]\rightarrow [l,r+1]$ 时,其新产生的贡献为 $a_{r+1}\times (f(a_{r+1},l,r)+1)+g(a_{r+1},l,r)$。
这个 $f$ 和 $g$ 都是满足差分性质的,即 $f(a,l,r)=f(a,1,r)-f(a,1,l-1);g(a,l,r)=g(a,1,r)-g(a,1,l-1)$。
这样就可以进行二次离线了。
用树状数组预处理出 $1\sim k$ 中比 $k$ 小的数的个数,和比 $k$ 大的数的和。
之后进行莫队时,我们需要一个 $O(1)$ 查询前/后缀和,$O(\sqrt n)$ 单点修改的数据结构,显然一个分块就好了。
总时间复杂度 $O(n\sqrt n)$。
Code:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#define bel(x)((x-1)/317+1)
using namespace std;
typedef long long LL;
const int N=5e5+5,siz=800,blocks=bel(100000);
int pre[N],suf[N],a[N];
LL prec[N],sufc[N],B2[100005];
int B1[100005],n,m;
LL ans[N];
inline void add1(int i){for(;i<=100000;i+=i&-i)++B1[i];}
inline int ask1(int i){int x=0;for(;i;i&=i-1)x+=B1[i];return x;}
inline void add2(int i,int x){for(;i<=100000;i+=i&-i)B2[i]+=x;}
inline LL ask2(int i){LL x=0;for(;i;i&=i-1)x+=B2[i];return x;}
struct que{
int l,r,id;
inline bool operator<(const que&rhs)const{return(l/siz!=rhs.l/siz?l<rhs.l:r<rhs.r);}
}q[N];
struct vue{
int l,r,id,fh;
};
vector<vue>L[N],R[N];
int bc[N],tc[320];LL bs[N],ts[320];
inline void update(int x){
int bL=bel(x-1);
for(int i=1;i<bL;++i)ts[i]+=x;
for(int i=(bL-1)*317+1;i<x;++i)bs[i]+=x;
bL=bel(x+1);
for(int i=bL+1;i<=blocks;++i)++tc[i];
for(int i=x+1;i<=bL*317&&i<=100000;++i)++bc[i];
}
inline int getcnt(int x){return bc[x]+tc[bel(x)];}
inline LL getmx(int x){return bs[x]+ts[bel(x)];}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i)
cin>>a[i];
LL sm=0;
for(int i=1;i<=n;++i){
pre[i]=ask1(a[i]-1);
add1(a[i]),add2(a[i],a[i]);
sm+=a[i];
prec[i]=sm-ask2(a[i])+a[i];
}
sm=0;
memset(B1,0,sizeof B1);
memset(B2,0,sizeof B2);
for(int i=n;i;--i){
suf[i]=ask1(a[i]-1);
add1(a[i]),add2(a[i],a[i]);
sm+=a[i];
sufc[i]=sm-ask2(a[i])+a[i];
}
for(int i=1;i<=m;++i)
cin>>q[i].l>>q[q[i].id=i].r;
sort(q+1,q+m+1);
for(int i=1,l=1,r=0;i<=m;++i){
if(r<q[i].r)L[l].push_back((vue){r+1,q[i].r,q[i].id,1});
if(r>q[i].r)L[l].push_back((vue){q[i].r+1,r,q[i].id,-1});
r=q[i].r;
if(l>q[i].l)R[r].push_back((vue){q[i].l,l-1,q[i].id,1});
if(l<q[i].l)R[r].push_back((vue){l,q[i].l-1,q[i].id,-1});
l=q[i].l;
}
for(int i=1;i<=n;++i){
for(int x=0;x<L[i].size();++x){
int l=L[i][x].l,r=L[i][x].r,id=L[i][x].id;
LL s=0;
for(int p=l;p<=r;++p)
s+=(LL)a[p]*(pre[p]-getcnt(a[p]))+prec[p]-getmx(a[p]);
ans[id]+=s*L[i][x].fh;
}
update(a[i]);
}
memset(tc,0,sizeof tc),memset(ts,0,sizeof ts);
memset(bc,0,sizeof bc),memset(bs,0,sizeof bs);
for(int i=n;i;--i){
for(int x=0;x<R[i].size();++x){
int l=R[i][x].l,r=R[i][x].r,id=R[i][x].id;
LL s=0;
for(int p=l;p<=r;++p)
s+=(LL)a[p]*(suf[p]-getcnt(a[p]))+sufc[p]-getmx(a[p]);
ans[id]+=s*R[i][x].fh;
}
update(a[i]);
}
for(int i=2;i<=m;++i)
ans[q[i].id]+=ans[q[i-1].id];
for(int i=1;i<=m;++i)
cout<<ans[i]<<'\n';
return 0;
}