莫队1 走上骗分之路
莫队1 走上骗分之路
新坑介绍莫队, 第一篇是不带修的线性莫队.
什么是莫队
一种硬往两边扩展 (可能是收缩) 的玄学算法, 是老前辈莫涛老师发明的算法, 又因为莫老师进了国家队, 所以叫莫队.
Google搜索需要搜索"Mo's Algo".
莫队能解决什么问题
很多, 只要 \([l, r]\) 这个区间的答案可以 \(O(1)\) 扩展到 \([l - 1, r]\), \([l + 1, r]\), \([l, r - 1]\) 和 \([l, r + 1]\) 这四个相邻区间的答案就可以用.
举例区间和
给一个数组和很多区间, 不带修, 求这些区间的和.
这个题显然是前缀和板子, 但是我们在讨论莫队, 所以只考虑莫队.
由于没这个题, 所以给个样例, 强度不知道大不大.
9 5 2 3 5 7 11 13 17 19 23 1 5 2 6 3 9 1 9 5 5
28 39 95 100 11
首先, 这个题扩展到相邻区间只需要加加减减的, 显然 \(O(1)\), 那么莫队考虑的就是怎么让这些加加减减的次数更少.
莫队的想法是, 给询问排序, 先看左端点所在的块, 再看右端点.
qsort
在stdlib.h
或cstdlib
里, 用不习惯可以用sort(q + 1, q + Q + 1, cmp)
然后把cmp
的比较改掉, 返回 \(x - y\) 就改成 \(x < y\), 返回 \(-x + y\) 就改成 \(x > y\).
typedef struct _node { int l, r, i; } qryy; int cmp(const void *a, const void *b) { qryy *x = (qryy*)(a), *y = (qryy*)(b); if (pos[x->l] ^ pos[y->l]) { return pos[x->l] - pos[y->l]; } else { return x->r - y->r; } } void get_ans() { int l = 1, r = 0; qsort(q + 1, Q, sizeof(q[0]), cmp); f1 (i, 1, Q, 1) { while (q[i].l < l) { s += a[--l]; } while (l < q[i].l) { s -= a[l++]; } while (r < q[i].r) { s += a[++r]; } while (q[i].r < r) { s -= a[r--]; } ans[q[i].i] = s; } }
read(&N, &Q); M = pow(N, 0.5); f1 (i, 1, N, 1) { read(a + i); pos[i] = (i - 1) / M + 1; } f1 (i, 1, Q, 1) { read(&q[i].l, &q[i].r); q[i].i = i; } get_ans(); f1 (i, 1, Q, 1) { cout << ans[i] << endl; }
不过既然 \(l\) 和 \(r\) 看上去这么像队头和队尾, 那么也有可能莫队是莫涛老师の双端队列吧.
有的问题需要预处理
VJudge SPOJ LuoGu 双倍经验_VJudge 双倍经验_LuoGu
求区间 unique
长, 就是区间降重后的数量.
SDOI再次坐实板子巨多.
双倍经验的数据范围较大, 所以数组都看着双倍经验开即可.
我们先预处理出每个数上一次在哪出现, 下一次在哪出现, 然后用这个扩展即可.
void init() { f1 (i, 1, N, 1) { pre[i] = lst[a[i]]; lst[a[i]] = i; } f1 (i, 1, N, 1) { lst[a[i]] = N + 1; } f2 (i, N, 1, 1) { nxt[i] = lst[a[i]]; lst[a[i]] = i; } }
int cmp(const void *a, const void *b) { qryy *x = (qryy*)(a), *y = (qryy*)(b); if (pos[x->l] ^ pos[y->l]) { return pos[x->l] - pos[y->l]; } else { return x->r - y->r; } } void get_ans() { int l = 1, r = 0; qsort(q + 1, Q, sizeof(q[0]), cmp); f1 (i, 1, Q, 1) { while (q[i].l < l) { s += (nxt[--l] > r); } while (l < q[i].l) { s -= (nxt[l++] > r); } while (r < q[i].r) { s += (pre[++r] < l); } while (q[i].r < r) { s -= (pre[r--] < l); } ans[q[i].i] = s; } }
read(&N); M = pow(N, 0.5); f1 (i, 1, N, 1) { read(a + i); pos[i] = (i - 1) / M + 1; } init(); read(&Q); f1 (i, 1, Q, 1) { read(&q[i].l, &q[i].r); q[i].k = i; } get_ans(); f1 (i, 1, Q, 1) { cout << ans[i] << endl; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· c# 半导体/led行业 晶圆片WaferMap实现 map图实现入门篇
· 易语言 —— 开山篇