首页 > 其他分享 >莫队

莫队

时间:2024-04-18 17:25:11浏览次数:14  
标签:frac int MAXS sum sqrt 莫队

简介

莫队是一种离线求区间信息的算法,可以做到 \(O((N+Q) \cdot\sqrt{N})\)。莫队中使用了分块的思想。

首先考虑一个问题:给定一个长度为 \(N\) 序列 \(A\) 和 \(Q\) 次询问,每次询问查询区间 \([X_i,Y_i]\) 的和。(请先忘掉前缀和、线段树、树状数组什么的)

比如以下数据:

5 4
3 2 2 1 5
1 3
4 5
2 6
1 1
2 2

莫队的思想是:记录一个 \(l,r\) 和 \(sum\),其中 \(sum=\sum \limits_{i=l}^r A_i\)。每次求一个新区间时,不断移动 \(l,r\),并维护 \(sum\)。

如:

int l = 1, r = 0, sum = 0, x, y;

cin >> x >> y;
for(; l > x; sum += a[--l]) {
}
for(; r < y; sum += a[++r]) {
}
for(; l < x; sum -= a[l++]) {
}
for(; r > y; sum -= a[r--]) {
}
cout << sum << "\n";

可这样仍然是 \(O(N)\) 的。

所以,我们可以离线求解。我们可以对查询区间按某种方式排序,使得时间复杂度更优。而莫队的排序策略是这样的:

  • 如果 \(\lfloor \frac{X_i}{\sqrt N} \rfloor = \lfloor \frac{X_j}{\sqrt N} \rfloor\):
    • 如果 \(Y_i < Y_j\),那么排序后 \(i\) 放在 \(j\) 前面。
    • 否则排序后 \(j\) 放在 \(i\) 前面。
  • 如果 \(\lfloor \frac{X_i}{\sqrt N} \rfloor \ne \lfloor \frac{X_j}{\sqrt N} \rfloor\):
    • 如果 \(\lfloor \frac{X_i}{\sqrt N} \rfloor <\lfloor \frac{X_j}{\sqrt N} \rfloor\),那么排序后 \(i\) 放在 \(j\) 前面。
    • 否则排序后 \(j\) 放在 \(i\) 前面。

按照这样的方式排序,那么 \(l\) 在一个块中会移动 \(O(Q)\) 次,共移动 \(O(Q\cdot\sqrt N)\)
次,右端点在同一个块中都是递增的,所以在一个块中移动 \(O(N)\) 次,共移动 \(O(N \cdot \sqrt N)\) 次。总时间复杂度 \(O((N+Q) \cdot \sqrt N)\)。

莫队不仅能求解区间和,很多其他区间信息也能求解。

优化

可以发现,每次处理完一个块后,右端点都要从新移动回去,所以我们可以让所有偶数块右端点从小到大,奇数块从大到小,可以优化大约 \(\frac{1}{2}\) 的常数。

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXN = 200001, MAXS = 447, MAXQ = 200001;

struct Node {
  int l, r, id;
}s[MAXQ];

int n, q, a[MAXN];
ll sum, ans[MAXQ];

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> q;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  for(int i = 1; i <= q; ++i) {
    cin >> s[i].l >> s[i].r;
    s[i].id = i;
  }
  sort(s + 1, s + q + 1, [](const Node &a, const Node &b) { return (a.l / MAXS == b.l / MAXS ? (a.l / MAXS % 2 ? a.r > b.r : a.r < b.r) : a.l / MAXS < b.l / MAXS); });
  ll sum = 0;
  for(int i = 1, x = 1, y = 0; i <= q; ++i) {
    for(; x > s[i].l; sum += a[--x]) {
    }
    for(; y < s[i].r; sum += a[++y]) {
    }
    for(; x < s[i].l; sum -= a[x++]) {
    }
    for(; y > s[i].r; sum -= a[y--]) {
    }
    ans[s[i].id] = sum;
  }
  for(int i = 1; i <= q; ++i) {
    cout << ans[i] << "\n";
  }
  return 0;
}

标签:frac,int,MAXS,sum,sqrt,莫队
From: https://www.cnblogs.com/yaosicheng124/p/18143978

相关文章

  • 莫队
    莫队是一种对于询问做转移的算法。对于可以离线,运算可逆的题目。如果按题目给的顺序操作,可以被以下数据hack11nn11nn11nn11nn...时间复杂度\(O(n^2)\)。我们可以通过某一些排序来降低时间复杂度。首先,把这个序列分成\(\sqrt{n}\)块,每一块按右......
  • C116 莫队二次离线 P4887 莫队二次离线
    视频链接:     LuoguP4887【模板】莫队二次离线(第十四分块(前体))//莫队二次离线O(n*sqrt(n)+n*C(k,14))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<vector>usingnamespacestd;constintN=100005;......
  • 莫队
    查询区间每个数出现的个数,离线算法,O(nsqrt(n))一、普通莫队题目链接https://www.luogu.com.cn/problem/P2709题目大意题目代码#include<iostream>#include<algorithm>#include<cmath>#definelllonglongconstintN=5e4+5;usingnamespacestd;intn,m,k,block......
  • 莫队学习笔记
    目录普通莫队初遇——从暴力谈起困境——乱跑的指针优化——顺路而为之带修莫队参考资料普通莫队初遇——从暴力谈起我们通过一道例题来讨论普通莫队。题目链接。观察数据范围,一个很直接的想法是:开一个数组\(cnt\),\(cnt_i\)表示在询问的区间内数字\(i\)出现的次数。对于......
  • #莫队二次离线,根号分治#洛谷 5398 [Ynoi2018] GOSICK
    题目\(m\)组询问求\(\sum_{l\leqi,j\leqr}[a_i\bmoda_j==0],n,m,a_i\leq5\times10^5\)分析设\(f(l,r,x)\)表示\(i或j\in[1,x],i或j\in[l,r]\)时的答案,\(g_x\)表示\([1,x]\)的答案,根号的做法可以通过三秒由于涉及区间内的求值,需要在莫队的基础上二次离线,那......
  • C114 回滚莫队 歴史の研究
    视频链接:C114回滚莫队歴史の研究_哔哩哔哩_bilibili   LuoguAT_joisc2014_c歴史の研究//回滚莫队O(n^(3/2))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>usingnamespacestd;#defineLLlonglongconstintN=1000005;......
  • 分块与莫队
    不沾树的博客变短了好多。分块例题这道题显然可以使用线段树乱搞过去,不过为了给主角面子我们假设我们不会做。对于一些难以使用数据结构维护答案的序列问题,我们考虑暴力。但是暴力太慢了,于是人们提出了分块。分块,就是把序列分成许多的小段,通过一些神秘的处理实现优化暴力。......
  • C113 带修莫队 P1903 [国家集训队] 数颜色/维护队列
    视频链接:   LuoguP1903[国家集训队]数颜色/维护队列//带修莫队O(n^(5/3))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>usingnamespacestd;constintN=1000005;intn,m,B,mq,mr,a[N];intsum,cnt[N],ans[N];st......
  • C112 莫队算法 P1494 [国家集训队] 小 Z 的袜子
    视频链接:  LuoguP1494[国家集训队]小Z的袜子//普通莫队O(n*sqrt(n))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>usingnamespacestd;constintN=50005;intn,m,B,a[N];intsum,cnt[N],ans1[N],ans2[N];str......
  • C111【模板】莫队算法 P2709 小B的询问
    视频链接:C111【模板】莫队算法P2709小B的询问_哔哩哔哩_bilibili   LuoguP2709小B的询问//普通莫队O(n*sqrt(n))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>usingnamespacestd;constintN=50005;intn,m,k,B,a[N];......