二次离线莫队 学习笔记

一、什么是二次离线莫队?

二次离线莫队,简称为二离莫队,是由 lxl 提出的莫队算法。基于莫队 \(+\) 扫描线的思想,离线处理询问以降低时间复杂度。

若更新答案时间为 \(O(m)\),则它可以将 \(O(n\sqrt{n}k)\to O(n\sqrt{n}+nk)\) 优化时间。

二、适用场景

  • 可以莫队
  • 答案可以前缀和或者差分

三、例题

P5501 来者不拒,去者不追

我们先定义两个函数。

\(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(); 
}

::::

posted @ 2026-01-20 17:26  mo_mo_yu  阅读(0)  评论(0)    收藏  举报