简单离线算法专题

Update Log

2023.3.20 开坑莫队专题

1.莫队

莫队算法可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。

1.1 普通莫队

1.1.1 介绍

假设我们目前已知 [l,r] 的答案,那么我们可以在 O(1) 的时间复杂度内扩展到 [l1,r],[l+1,r],[l,r1],[l,r+1],我们就可以做到在 O(n×(n+q)) 的时间复杂度离线解决所有询问。

具体的来说,我们就是令块的大小 Sn,对于在不同块的左端点,那么我们就按块的先后顺序排序,否则我们按右端点排序。

bool cmp(Query A,Query B){
  return A.l/S!=B.l/S? A.l<B.l:A.r<B.r;
}

1.1.2 时间复杂度证明

显然的,左右端点的移动是互不干扰的,我们可以将其拆开计算。左端点将在同一个块中移动,每次询问最多会移动 n 次,复杂度 O(n×q)。右端点的话会从左到右移动 n 次,时间复杂度 O(n×n),得证。

1.1.3 例题

1.1.3.1 ABC293G[洛谷题面]

板子题

#include<bits/stdc++.h>
#define int long long
#define repe(i,l,r) for(int (i)=l;(i)<=r;(i)++)
#define rep(i,n) for(int (i)=1;(i)<=n;(i)++)
#define FOR(i,r,l) for(int (i)=r;(i)>=l;(i)--)
#define INF 0x3f3f3f
#define pii pair<int,int>
#define mpr make_pair
#define pb push_back
#define ALL(v) (v).begin(),(v).end()
#define rsort(v) sort(ALL(v),greater<int>())
#define lb(v,x) (int)(lower_bound(ALL(v),x)-v.begin())
#define ub(v,x) (int)(upper_bound(ALL(v),x)-v.begin())
#define uni(v) v.resize(unique(ALL(v))-v.begin())
#define debug(x) cerr<<#x<<':'<<x<<endl;
using namespace std;
int read(){int sum=0,f=1;char c;c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9'){sum=(sum<<1)+(sum<<3)+(c-'0');c=getchar();}return sum*f;}
void out(int x){if(x<0){x=-x;putchar('-');}if(x>=10)out(x/10);putchar(x%10+'0');}
template <typename T>void die(T s){cout<<s<<endl;exit(0);}
int fast(int a,int b,int P){int res=1;if(P<=0){while(b){if(b&1)res=res*a;a=a*a;b>>=1;}}else{while(b){if(b&1)res=res*a%P;a=a*a%P;b>>=1;}}return res;}
template <typename T>void chkmax(T& a,T b){if(a<b)a=b;return;}
template <typename T>void chkmin(T& a,T b){if(a>b)a=b;return;}
int n,q;
int a[200005],S;
int ans[200005];
struct node{
	int l,r,id;
};
vector<node> Query;
bool cmp(node a,node b){
	if(a.l/S!=b.l/S)return a.l/S<b.l/S;
	else return a.r>b.r;
}
int L=1,R,res;
int num[200005];
void del(int x){
	num[x]--;
	res-=(num[x]-1)*num[x]/2;
	return;
}
void add(int x){
	res+=num[x]*(num[x]-1)/2;
	num[x]++;
	return;
}
void solve(int l,int r){
	while(R<r)add(a[++R]);
	while(R>r)del(a[R--]);
	while(L>l)add(a[--L]);
	while(L<l)del(a[L++]);
	return;
}
signed main(){
	n=read(),q=read();
	S=sqrt(n);
	rep(i,n)a[i]=read();
	rep(i,q){
		int l=read(),r=read();
		Query.pb({l,r,i});
	}
	sort(ALL(Query),cmp);
	for(auto Q:Query){
		solve(Q.l,Q.r);
		ans[Q.id]=res;
	}
	rep(i,q)cout<<ans[i]<<endl;
	return 0;
}
posted @   ktq_cpp  阅读(32)  评论(1编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示