二次离线莫队 学习笔记
一、什么是二次离线莫队?
二次离线莫队,简称为二离莫队,是由 lxl 提出的莫队算法。基于莫队 \(+\) 扫描线的思想,离线处理询问以降低时间复杂度。
若更新答案时间为 \(O(m)\),则它可以将 \(O(n\sqrt{n}k)\to O(n\sqrt{n}+nk)\) 优化时间。
二、适用场景
- 可以莫队
- 答案可以前缀和或者差分
三、例题
我们先定义两个函数。
\(f(x,l,r)\) 表示 \(\sum_{i\in[l,r]}{[a_i<x]}\)。
\(g(x,l,r)\) 表示 \(\sum_{i\in[l,r]}{[a_i>x]\times a_i}\)。
发现对于区间 \([l,r]\),扩展到 \(r+1\) 时答案加上 \(a_{r+1}\times(f(a_{r+1},l,r)+1)+g(a_{r+1},l,r)\)。
发现 \(f,g\) 两个函数可以前缀和处理,即 \(f(x,l,r)=f(x,1,r)-f(x,1,l-1)\)。\(g\) 同理。那么我们可以使用树状数组预处理出 \(f,g\) 的前缀和。
发现前缀和后有一类贡献没有对齐,我们可以把它存下来,等莫队完成以后来维护。
由于移动了 \(n\sqrt{n}\) 次,所以我们需要维护一个 \(O(1)\) 查询的数据结构。根号配根号,我们可以使用分块维护。
我们成功把 \(O(n\sqrt{n}\log_2 n)\) 的算法优化到了 \(O(n\sqrt{n})\)。
\(\texttt{trick}\):查询会有 \(n\sqrt{q}\) 个,发现是连续的,可以一起存储,用局部变量算答案。
::::info[代码]
#include<bits/stdc++.h>
#define int long long
#define ll long long
#define ull unsigned long long
#define int128 __int128
#define lowbit(x) (x&-x)
#define faster ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int maxn=5e5+10;
const int maxB=1010;
int n,q;
int a[maxn];
ll f[maxn];
int maxA;
struct node{
int l,r,id;
}e[maxn];
struct edge{
int l,r;
char w;
int id;
};
vector<edge>t[maxn];
int bsize,b[maxn];
bool cmp(node &x,node &y){
return b[x.l]!=b[y.l]?b[x.l]<b[y.l]:(b[x.l]&1?x.r<y.r:x.r>y.r);
}
int tot;
struct Blocks{//分块
ll sum[maxn];
int sum2[maxn];
void build(){
for(int i=1;i<=maxA;i++)b[i]=(i+bsize-1)/bsize;
for(int i=0;i<maxn;i++)sum[i]=0;
for(int i=0;i<maxn;i++)sum2[i]=0;
}
void modify(int x,int val){
int tmp=b[x];
for(int i=tmp;i<=b[maxA];i++)sum[i]+=val;
for(int i=x;i<=min(maxA,tmp*bsize);i++)sum2[i]+=val;
}
int query(int x){
return sum[b[x]-1]+sum2[x];
}
}blo,blo2;
ll out[maxn];
ll ans;
void solve(){
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i],maxA=max(maxA,a[i]);
maxA+=3;
tot--;
bsize=sqrt(maxA);
blo.build(),blo2.build();
for(int i=1;i<=n;i++){
blo.modify(a[i],1);
blo2.modify(a[i],a[i]);
f[i]=blo.query(a[i]-1)*a[i]+blo2.query(maxA)-blo2.query(a[i]);
}
for(int i=1;i<=q;i++){
cin>>e[i].l>>e[i].r;
e[i].id=i;
}
bsize=max(1.0,n/sqrt(q));
for(int i=1;i<=n;i++)b[i]=(i+bsize-1)/bsize;
sort(e+1,e+q+1,cmp);
int L=1,R=0;
for(int i=1;i<=q;i++){//为了算答案,只处理转移,最后前缀和统计答案。
ans=0;//转移答案(upd_ans)
int l=e[i].l,r=e[i].r;
if(L>l)t[R].push_back({l,L-1,1,i});
while(L>l){
if(L-1>=0)ans-=f[L-1];
// if(R)t[R].push_back({L-1,1,id});
--L;
}
if(R<r)t[L-1].push_back({R+1,r,-1,i});
while(R<r){
if(R+1>=0)ans+=f[R+1];
// if(L>1)t[L-1].push_back({R+1,-1,id});
++R;
}
if(L<l)t[R].push_back({L,l-1,-1,i});
while(L<l){
++L;
if(L-1>=0)ans+=f[L-1];
// if(R)t[R].push_back({L-1,-1,id});
}
if(R>r)t[L-1].push_back({r+1,R,1,i});
while(R>r){
--R;
if(R+1>=0)ans-=f[R+1];
// if(L>1)t[L-1].push_back({R+1,1,id});
}
out[i]=ans;
}
bsize=sqrt(maxA);
blo.build();
blo2.build();
for(int j=1;j<=n;j++){
blo.modify(a[j],1);
blo2.modify(a[j],a[j]);
for(int i=0;i<(int)t[j].size();i++)
for(int p=t[j][i].l;p<=t[j][i].r;p++)
out[t[j][i].id]+=(blo.query(a[p]-1)*a[p]+blo2.query(maxA)-blo2.query(a[p]))*t[j][i].w;
}
for(int i=1;i<=n;i++)f[i]=f[i-1]+a[i];
for(int i=1;i<=q;i++)out[i]+=out[i-1],blo.sum[e[i].id]=out[i]+(f[e[i].r]-f[e[i].l-1]);
for(int i=1;i<=q;i++)cout<<blo.sum[i]<<'\n';
}
signed main(){
faster;
int T=1;
while(T--)solve();
}
::::

浙公网安备 33010602011771号