莫队

1|0普通莫队

普通莫队由莫涛总结并实现。可以在 O(nn) 的时间复杂度内解决不带修的区间问题。

那什么样的题才能用莫队呢?

最重要的特征是知道区间 [l,r] 的答案,可以 O(1) 得知 [l1,r][l,r1][l+1,r][l,r+1] 区间的答案。

先来看一个例子:

给出序列 {an}m 次询问,每次求区间 [li,ri] 中的不同数数量。

首先强制离线。

考虑怎么从区间 [l,r] 得到其相邻四个区间的答案。

如果当前区间是 [1,2],那我们扩展到区间 [1,3] 只需要判断 a3 是否在 [1,2] 中出现了,如果出现了,那么和 [1,2] 的答案一样,如果没出现,那么久多出现了一个,答案就是 [1,2] 的答案加一。

减法同理。

但是现在在极限数据下仍然是 O(nm) 的时间复杂度。

怎么优化呢?

莫涛用了如下方法:

分块!

对离线下来的数据排序按左右端点的块编号排序,时间复杂度证明可以参考 oi-wiki

给出参考代码:

点击查看代码
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 100; int n, a[N], val, cnt[N], m, id[N], p[N], k; void add(int u) { if (!cnt[a[u]]) { val++; } cnt[a[u]]++; } void del(int u) { cnt[a[u]]--; if (!cnt[a[u]]) { val--; } } struct Node{ int l, r, id; } q[N]; bool cmp(Node i, Node j) { if (id[i.l] != id[j.l]) { return id[i.l] < id[j.l]; } return i.r < j.r; } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; } int t = sqrt(n); for (int i = 1; i <= n; i++) { id[i] = (i - 1) / t + 1; } for (int i = 1; i <= m; i++) { cin >> q[i].l >> q[i].r; q[i].id = i; } sort(q + 1, q + m + 1, cmp); int l = 1, r = 0; for (int i = 1; i <= m; i++) { while (l > q[i].l) { add(--l); } while (r < q[i].r) { add(++r); } while (l < q[i].l) { del(l++); } while (r > q[i].r) { del(r--); } p[q[i].id] = val; } for (int i = 1; i <= m; i++) { cout << p[i] << endl; } return 0; }

2|0带修莫队

带修莫队在普通莫队的基础上增加了一维时间。

具体做法如下:

记录修改时的时间(及输入的编号)。

排序时和普通莫队一样,但是多了判断时间大小。

询问的时候:如果当前修改操作位置在当前范围之内,那么改变修改位置的值。

eg:codeforces 940F Machine Learning

先对数组离散化。莫队维护一个桶和答案数组。

点击查看代码
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 1e6 + 100; int n, m, id[N], a[N], opt, qcnt, pcnt, b[N], tot, cnt[N], num[N], now, ans[N]; struct Node{ int l, r, t, d; const bool operator < (const Node& e) const { if (id[l] != id[e.l]) { return id[l] < id[e.l]; } if (id[r] != id[e.r]) { return id[r] < id[e.r]; } return t < e.t; } } q[N]; struct Edge{ int pos, val; } p[N]; void add(int x) { cnt[x]++; num[cnt[x] - 1]--; num[cnt[x]]++; } void del(int x) { cnt[x]--; num[cnt[x] + 1]--; num[cnt[x]]++; } void answer() { now = 1; while (num[now]) { now++; } } void modify(int now, int i) { if (q[i].l <= p[now].pos && p[now].pos <= q[i].r) { del(a[p[now].pos]); add(p[now].val); } swap(a[p[now].pos], p[now].val); } signed main() { cin >> n >> m; int len = pow(n, 0.666); for (int i = 1; i <= n; i++) { cin >> a[i]; id[i] = (i - 1) / len + 1; b[++tot] = a[i]; } while (m--) { cin >> opt; if (opt == 1) { qcnt++; cin >> q[qcnt].l >> q[qcnt].r; q[qcnt].t = pcnt; q[qcnt].d = qcnt; } else { pcnt++; cin >> p[pcnt].pos >> p[pcnt].val; b[++tot] = p[pcnt].val; } } sort(b + 1, b + tot + 1); int m = unique(b + 1, b + tot + 1) - b - 1; for (int i = 1; i <= tot; i++) { if (i <= n) { a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b; } else { p[i - n].val = lower_bound(b + 1, b + tot + 1, p[i - n].val) - b; } } sort(q + 1, q + qcnt + 1); int l = 1, r = 0, t = 0; for (int i = 1; i <= qcnt; i++) { while (q[i].l < l) { add(a[--l]); } while (q[i].r > r) { add(a[++r]); } while (q[i].l > l) { del(a[l++]); } while (q[i].r < r) { del(a[r--]); } while (t < q[i].t) { modify(++t, i); } while (t > q[i].t) { modify(t--, i); } answer(); ans[q[i].d] = now; } for (int i = 1; i <= qcnt; i++) { cout << ans[i] << endl; } return 0; }

__EOF__

本文作者ようこそ!
本文链接https://www.cnblogs.com/ydq1101/p/17833527.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   ydq1101  阅读(31)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示