\(\text{莫队是一种离线算法。}\)php
\(\text{莫队 = 分块 + 暴力}\)html
借用的内容 https://blog.csdn.net/u011815404/article/details/88317786 https://www.cnblogs.com/WAMonster/p/10118934.html https://blog.csdn.net/Enzymii/article/details/77905451 https://blog.csdn.net/wangqianqianya/article/details/89409522 https://www.myblog.link/2016/01/26/MO-s-Algorithm/ https://blog.csdn.net/huayunhualuo/article/details/52153449 https://blog.csdn.net/a1351937368/article/details/78429044 https://blog.csdn.net/qq_38891827/article/details/82190013 https://blog.csdn.net/chenxiaoran666/article/details/81253315 https://blog.csdn.net/chenxiaoran666/article/details/81251960 https://blog.csdn.net/Runner__/article/details/51398047
通常的区间问题均可以使用莫队。
\(\text{莫队的灵魂在于:若是你知道了[L,R]的答案。你能够在O(1)的时间下获得[L,R-1]和[L,R+1]和[L-1,R]和[L+1,R]的答案的话。就能够使用莫队算法。}\)node
\(\text{前置芝士:分块 , sort , LCA/树剖(树上莫队)}\)c++
\(\text{莫队就是把全部的询问先存下来 排完序一个个玩}\)
git
\(\text{大概就是这个样子:若是一段区间是l - r的 那么左指针仍是留在l 右指针是留在r的}\)
\(\text{对于下一次操做:莫队会把 上一次左边的位置 移到这一次的位置 右边也同样}\)
\(\text{这样的话 对于朴素暴力已经有了足够的优化 可是仍是很慢 最坏状况仍是(N*M)的}\)
\(\text{咱们考虑排序:把这个按左端点排序 左端点相同时按右端点排序 这样的话是能够证实的优化 由于左端点只会往右}\)
\(\text{对于一种排序我不会证实其复杂度 不过好像真的快不少呢}\)
(转自https://blog.csdn.net/u011815404/article/details/88317786)算法
int l=1,r=0,ans=0; for(int i=1;i<=m;i++){ while(l>q[i].l) add(--l);//[l-1,r] while(l<q[i].l) del(l++);//[l+1,r] while(r<q[i].r) add(++r);//[l,r+1] while(r>q[i].r) del(r--);//[l,r-1] res[q[i].id]=ans;//存储答案 }
这是离线莫队的裸的板子(真的就这么短的四句话只是add和del里面要加内容。。)优化
至于块的大小在这儿spa
对于复杂度的分析
\(\text{对于左指针:咱们考虑最坏状况:莫队的添加删除是O(1)的 那么处理块i的最坏复杂度是$O(x_i \sqrt(n))$}\)
\(\sum_{i = 1}^{n} x_i * \sqrt{n} + n * \sqrt{n}\) = \(O(n\sqrt{n})\)
\(\text{对于右指针:若是咱们按照右端点排序 最坏状况显然是O(n)的即从1跳到n}\)
由此能够推出 莫队的复杂度大概就是一个 \(\theta(n \sqrt{n})\)
莫队代码都很短的 只要别把 L++
写成 ++L
.net
例题整理 https://www.lydsy.com/JudgeOnline/problem.php?id=2038 小Z的袜子 https://www.lydsy.com/JudgeOnline/problem.php?id=4540 https://www.luogu.org/problem/P1494 小Z的袜子(双倍经验) http://codeforces.com/contest/617/problem/E https://www.spoj.com/problems/DQUERY/en/ http://codeforces.com/problemset/problem/86/D http://acm.hdu.edu.cn/showproblem.php?pid=5213 http://acm.hdu.edu.cn/showproblem.php?pid=5381 http://acm.fzu.edu.cn/problem.php?pid=2226 http://acm.hdu.edu.cn/showproblem.php?pid=4638 http://acm.hdu.edu.cn/showproblem.php?pid=4676 带修改的莫队 https://www.luogu.org/problem/P1903 离散化 https://www.lydsy.com/JudgeOnline/problem.php?id=3289 树上莫队 https://www.lydsy.com/JudgeOnline/problem.php?id=1086
bzoj4866
bzoj3809 还有在衢州欠下的题(小声指针
莫队的板子:https://blog.csdn.net/wangqianqianya/article/details/89409522
例题先鸽 明天再更
https://www.lydsy.com/JudgeOnline/problem.php?id=2038 https://www.luogu.org/problem/P1494
这题好像就是个结论题
数学计算方法为
对于一个区间的计算咱们设\(i\)的个数为\(s_i\)
那么答案就是
$\sum_{i = 1}^{n} {s_i * (s_i -1)} - (r - l + 1) $
\(-------------------\)
${ (r - l) * (r - l + 1) } $
代码
#include <bits/stdc++.h> using namespace std ; #define int long long inline int read() { register int res = 0 ; int f = 1 ;register char c = getchar() ; for( ; !isdigit(c) ; c = getchar()) if(c == '-') f = -1 ; for( ; isdigit(c) ; c = getchar()) res = (res << 1) + (res << 3) + (c & 15) ; return res * f ; } struct node { int l , r ; int id ; } ; // #define int long long struct Answer { int x , y ; inline int gcd(int x , int y) { return y == 0 ? x : gcd(y , x % y) ; } inline void Solve() { if(! x) { x = 0 ; y = 1 ; } else { int g = gcd(x , y) ; x /= g ; y /= g ; } printf("%lld/%lld\n" , x , y) ; return ; } }; const static int N = 100000 + 5 ; int n ; int a[N] ; node q[N] ; int bl[N] ; Answer Ans[N] ; inline bool cmp(node x , node y) { return bl[x.l] ^ bl[y.l] ? x.r < y.r : x.l < y.l ; } int ans = 0 ; int s[N] ; inline void Delete(int x) { ans -= s[a[x]] * s[a[x]] ; s[a[x]] -- ; ans += s[a[x]] * s[a[x]] ; } inline void Insert(int x) { ans -= s[a[x]] * s[a[x]] ; s[a[x]] ++ ; ans += s[a[x]] * s[a[x]] ; } signed main() { n = read() ; int m = read() ; int unt = sqrt(n) ; for(register int i = 1 ; i <= n ; i ++) a[i] = read() ; for(register int i = 1 ; i <= n ; i ++) bl[i] = (i - 1) / unt + 1 ; for(register int i = 1 ; i <= m ; i ++) { int l = read() ; int r = read() ; q[i] = { l , r , i} ; } sort(q + 1 , q + m + 1 , cmp) ; int l = 1 , r = 0 ; int ans_x = 0 ; int ans_y = 0 ; for(register int i = 1 ; i <= m ; i ++) { for( ; l < q[i].l ; Delete(l ++)) ; for( ; l > q[i].l ; Insert(-- l)) ; for( ; r < q[i].r ; Insert(++ r)) ; for( ; r > q[i].r ; Delete(r --)) ; if(q[i].l == q[i].r) { Ans[q[i].id] = {0 , 1} ; continue ; } ans_x = ans - (q[i].r - q[i].l + 1) ; ans_y = (q[i].r - q[i].l + 1) * (q[i].r - q[i].l) ; Ans[q[i].id] = {ans_x , ans_y} ; } for(register int i = 1 ; i <= m ; i ++) { Ans[i].Solve() ; } return 0 ; }