【模板】莫队算法

tech2023-12-14  32

前往:我自己搭建的博客

题目

洛谷P2709小B的询问

题解

莫队算法要求询问可以离线,并且要求被询问的区间信息便于从邻近区间转移。它是一种优化的暴力,通过将询问进行特殊的排序(方法有很多种),使得相似的询问排在一起,可以批量处理。被询问区间的排序方式:需要用到分块思想(将一段序列分成几段),所有被询问区间的左端点所在的分块的编号递增(即被询问区间的左端点大体呈递增趋势),每个分块内的区间右端点单调,且相邻分块的单调性相反(即被询问区间的右端点大体呈波浪形)。分块大小和排序方式的原因玄学,无法理论上求最优方式,只能测试。

代码

#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=5e4+5; int n,m,k,block; ll ans; int a[maxn],belong[maxn],sum[maxn]; //sum[i]表示数字i出现的次数,belong[i]表示位置i属于的分块 struct ask{int l,r,id; ll ans;}q[maxn]; inline int read() { int s=0; char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar(); return s; } inline bool cmp1(const ask &x,const ask &y) { return belong[x.l]^belong[y.l] ? belong[x.l]<belong[y.l] : belong[x.l]&1 ? x.r>y.r : x.r<y.r; } inline bool cmp2(const ask &x,const ask &y) {return x.id<y.id;} inline void add(int x) //向当前区间中加入一个数字x { ans-=(ll)sum[x]*sum[x]; sum[x]++; ans+=(ll)sum[x]*sum[x]; } inline void del(int x) //从当前区间中删掉一个数字x { ans-=(ll)sum[x]*sum[x]; sum[x]--; ans+=(ll)sum[x]*sum[x]; } int main() { n=read(),m=read(),k=read(); block=n/sqrt(m*2/3); for(int i=1;i<=n;i++) a[i]=read(),belong[i]=(i-1)/block+1; for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i; sort(q+1,q+m+1,cmp1); int l=1,r=0; for(int i=1;i<=m;i++) { while(l>q[i].l) add(a[--l]); while(l<q[i].l) del(a[l++]); while(r<q[i].r) add(a[++r]); while(r>q[i].r) del(a[r--]); q[i].ans=ans; } sort(q+1,q+m+1,cmp2); for(int i=1;i<=m;i++) printf("%lld\n",q[i].ans); return 0; }

 

最新回复(0)