算法简介:赤沙的归嗣 莫队算法是由莫涛提出的算法。在莫涛提出莫队算法之前,莫队算法已经在 Codeforces 的高手圈里小范围流传,但是莫涛是第一个对莫队算法进行详细归纳总结的人。莫涛提出莫队算法时,只分析了普通莫队算法,但是经过 OIer 和 ACMer 的集体智慧改造,莫队有了多种扩展版本。莫队算法之所以叫做莫队算法,是因为莫涛是当时的国家队队长,至今莫队算法依然被称为 Mo's Algorithm
。
莫队算法可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。
算法试用:贯月的耀锋 算法推导:织狩的奉祀 莫队算法是一种基于分块的算法。其原理在于对于所有的询问离线,而离线后我们对于每一个询问所在的左右端点的位置进行分块,首先对于左端点排序,如果不在同一个块,我们就可以比较左端点,如果在一起,我们就要看左端点所在块的奇偶性。如果块是奇数,r 按从小到大排序,如果块是偶数,r 从大到小排序,这样我们的 r 指针在处理完这个奇数块的问题后,将在返回的途中处理偶数块的问题,再向 n 移动处理下一个奇数块的问题,优化了 r 指针的移动次数,一般情况下,这种优化能让程序快很多很多。(我曾在洛谷 P1972 用此方法把 40pts 改到了 50pts )
然后我们暴力的去找到当前的询问区间,这里就需要莫队的必要条件:操作可 O(1) 在边界拓展 1 。由此性质,代码也不会繁杂。
但是值得注意的是,莫队算法经行拓展的时候左右端点的移动方式会直接影响正确性,详见 $\tt OI\ Wiki$
算法演示:守戍的誓命 这个是我的 P1972 的莫队代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 #include <cstdio> #include <iostream> #include <cmath> #include <algorithm> namespace FastIO { char buf[110 ], *p1, *p2; #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2) ? EOF : *p1++) template <typename T> inline T read (T& x) { x = 0 ; int f = 1 ; char ch; while (!isdigit (ch = getchar ())) if (ch == '-' ) f = -1 ; while (isdigit (ch)) x = (x << 1 ) + (x << 3 ) + (ch ^ 48 ), ch = getchar (); x *= f; return x; } template <typename T, typename ... Args> inline void read (T& x, Args &...x_) { read (x); read (x_...); return ; } }; using namespace FastIO;const int N = 1e6 + 10 ;int n, Q;int a[N];int B, cnt[N], ans;int pos[N], res[N];struct Qiz { int l, r, id; inline bool operator < (const Qiz &x) const { if (pos[l] != pos[x.l]) return l < x.l; return (pos[l] & 1 ) ? r < x.r : r > x.r; } }qs[N]; inline void Input () { read (n); B = (int )pow (n, 0.5 ); for (int i = 1 ; i <= n; i++) { read (a[i]); pos[i] = i / B; } read (Q); for (int i = 1 ; i <= Q; i++) { read (qs[i].l, qs[i].r); qs[i].id = i; } return ; } inline void Upd (int x, int f) { x = a[x]; cnt[x] += f; if (cnt[x] == 0 && f == -1 ) return (void )ans--; if (cnt[x] == 1 && f == 1 ) return (void )ans++; return ; } inline void Work () { std::sort (qs + 1 , qs + Q + 1 ); for (int i = 1 , l = 1 , r = 0 ; i <= Q; i++) { const Qiz &q = qs[i]; while (l > q.l) Upd (--l, +1 ); while (r < q.r) Upd (++r, +1 ); while (l < q.l) Upd (l++, -1 ); while (r > q.r) Upd (r--, -1 ); res[q.id] = ans; } for (int i = 1 ; i <= Q; i++) { printf ("%d\n" , res[i]); } return ; } int main () { int T = 1 ; while (T--) { Input (); Work (); } return 0 ; }
算法实战:萦金的苍瞳 查看相关算法标签喵!