莫队笔记
莫队的主要思想是将询问离线,并通过分块合理利用询问之间的公共信息,高效率地求解问题。
普通莫队
P3901 数列找不同
考虑记 $c_i$ 表示 $i$ 的出现次数,并在移动左右端点的同时维护 $c$ 数组以及 $res$。
于是就做完了。
注意正确的顺序应该是 l--,r--,r++,l++,可以奇偶排序优化。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 15;
inline int read() {
bool f = 0;
char ch = getchar_unlocked();
while (ch < '0' || ch > '9') {
if (ch == '-') f = 1;
ch = getchar_unlocked();
}
int x = 0;
while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar_unlocked();
return f ? -x : x;
}
int n, q, a[N], B;
struct node {
int l, r;
int id;
} b[N];
inline bool cmp(node a, node b) {
if (a.l / B != b.l / B) return a.l < b.l;
if ((a.l / B) & 1) return a.r < b.r;
return a.r > b.r;
}
bool ans[N];
int c[N], cnt = 0;
inline void add(int x) { c[a[x]]++, cnt += (c[a[x]] == 2); }
inline void del(int x) { c[a[x]]--, cnt -= (c[a[x]] == 1); }
int main() {
n = read(), q = read(), B = sqrt(n);
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= q; i++) b[i].l = read(), b[i].r = read(), b[i].id = i;
sort(b + 1, b + 1 + q, cmp);
int l = 1, r = 1; c[a[1]]++;
for (int i = 1; i <= q; i++) {
while (l > b[i].l) add(--l);
while (r > b[i].r) del(r--);
while (r < b[i].r) add(++r);
while (l < b[i].l) del(l++);
ans[b[i].id] = (cnt == 0);
}
for (int i = 1; i <= q; i++) puts(ans[i] ? "Yes" : "No");
return 0;
}
P1494 [国家集训队] 小 Z 的袜子
P2709 小B的询问
树上莫队
考虑把括号序列整出来(为什么大部分题解都说这是欧拉序??)然后在上面干一些神秘的事情。
COT2 - Count on a tree II
分类讨论 $(u,v)$ 是否有祖孙关系,这里令 $in_u \lt in_v$。
若是祖孙关系,则 $[in_u,in_v]$,否则 $[out_u,in_v]$。
然后正常莫队。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 15, M = N << 1;
int n, q, B, a[N], b[N], n1;
int h[N], e[M], ne[M], idx = 0;
void addedge(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; }
int sz[N], son[N], fa[N], dep[N];
void dfs1(int u, int father) {
sz[u] = 1, fa[u] = father, dep[u] = dep[father] + 1;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == father) continue;
dfs1(v, u);
sz[u] += sz[v];
if (!son[u] || (sz[son[u]] < sz[v])) son[u] = v;
}
}
int in[N], out[N], tot = 0;
int top[N];
int dfn[N << 1];
void dfs2(int u, int t) {
dfn[++tot] = u, in[u] = tot;
top[u] = t;
if (son[u]) dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
dfn[++tot] = u, out[u] = tot;
}
int lca(int u, int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
return u;
}
struct node {
int l, r, id;
int up; //lca
} p[N];
bool cmp(node a, node b) {
if (a.l / B != b.l / B) return a.l < b.l;
if ((a.l / B) & 1) return a.r < b.r;
return a.r > b.r;
}
int ans[N];
bool st[N]; //异或对消
int c[N], res = 0;
void add(int x) { c[a[x]]++, res += (c[a[x]] == 1); }
void del(int x) { c[a[x]]--, res -= (c[a[x]] == 0); }
void get(int x) {
if (st[dfn[x]]) del(dfn[x]);
else add(dfn[x]);
st[dfn[x]] ^= 1;
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i], h[i] = -1;
//离散化
sort(b + 1, b + 1 + n), n1 = unique(b + 1, b + 1 + n) - b - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + 1 + n1, a[i]) - b;
// for (int i = 1; i <= n; i++) cout << a[i] << ' '; puts("");
//build tree
for (int i = 1, u, v; i < n; i++) {
scanf("%d%d", &u, &v);
addedge(u, v), addedge(v, u);
}
dfs1(1, 0), dfs2(1, 1);
B = sqrt(tot);
for (int i = 1, u, v; i <= q; i++) {
scanf("%d%d", &u, &v);
if (in[u] > in[v]) swap(u, v);
int l = lca(u, v);
if (l == u) { //u is v's grandfather
//in[u] -> in[v]
p[i] = (node){in[u], in[v], i, 0};
} else { //u -> l -> v
//out[u] -> in[v]
p[i] = (node){out[u], in[v], i, l};
}
}
sort(p + 1, p + 1 + q, cmp);
int l = 1, r = 1; c[a[1]]++, st[1] = 1, res++;
for (int i = 1; i <= q; i++) {
while (r < p[i].r) get(++r);
while (r > p[i].r) get(r--);
while (l < p[i].l) get(l++);
while (l > p[i].l) get(--l);
int up = p[i].up;
if (up) c[a[up]]++, res += (c[a[up]] == 1);
ans[p[i].id] = res;
if (up) c[a[up]]--, res -= (c[a[up]] == 0);
}
for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
return 0;
}
带修莫队
P1903 [国家集训队] 数颜色 / 维护队列
多一维时间罢了,注意先修改 l,r 再修改 t。
注意在移动时间轴的时候要特判在区间内再修改答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 140015, M = 1e6 + 15;
int n, q, a[N], B, ans[N];
int cntC = 0, cntQ = 0;
pair<int, int> change[N];
struct node {
int l, r, t, id;
} p[N << 1];
bool cmp(node a, node b) {
if ((a.l - 1) / B != (b.l - 1) / B) return a.l < b.l;
if ((a.r - 1) / B != (b.r - 1) / B) return a.r < b.r;
return a.t < b.t;
}
int c[M], res = 0;
void add(int x) { c[a[x]]++, res += (c[a[x]] == 1); }
void del(int x) { c[a[x]]--, res -= (c[a[x]] == 0); }
void work(int l, int r, int t) {
int pos = change[t].first, val = change[t].second;
if (l <= pos && pos <= r) {
c[a[pos]]--, res -= (c[a[pos]] == 0);
c[val]++, res += (c[val] == 1);
}
swap(a[pos], change[t].second);
}
int main() {
scanf("%d%d", &n, &q), B = pow(n, 0.666666);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= q; i++) {
char opt; scanf(" %c", &opt);
if (opt == 'R') {
int x, y; scanf("%d%d", &x, &y);
change[++cntC] = make_pair(x, y);
} else { //'Q'
int l, r; scanf("%d%d", &l, &r);
p[++cntQ] = (node){l, r, cntC, cntQ};
}
}
sort(p + 1, p + 1 + cntQ, cmp);
int l = 1, r = 1, t = 0;
c[a[1]]++, res = 1;
for (int i = 1; i <= cntQ; i++) {
while (r > p[i].r) del(r--);
while (r < p[i].r) add(++r);
while (l > p[i].l) add(--l);
while (l < p[i].l) del(l++);
while (t < p[i].t) work(l, r, ++t);
while (t > p[i].t) work(l, r, t--);
ans[p[i].id] = res;
}
for (int i = 1; i <= cntQ; i++) printf("%d\n", ans[i]);
return 0;
}
回滚莫队
P5906 【模板】回滚莫队&不删除莫队
左端点在同一块内则右端点升序。
这样右端点不断往右走,左端点不断在块内反复横跳,可以保证复杂度。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 15, INF = 0x3f3f3f3f;
int n, n1, q, a[N], b[N], B;
int ans[N];
struct node {
int l, r, id;
} p[N];
bool cmp(node a, node b) {
if ((a.l - 1) / B != (b.l - 1) / B) return a.l < b.l;
return a.r < b.r;
}
int Max[N], Min[N];
int pmx[N], pmn[N];
int get(int l, int r) {
int res = 0;
for (int i = r; i >= l; i--) Max[a[i]] = max(Max[a[i]], i), res = max(res, Max[a[i]] - i);
for (int i = l; i <= r; i++) Max[a[i]] = 0;
return res;
}
bool clr[N];
int res = 0, lst = 0;
void add(int x, bool need) { //need==1 ---> 需要删除
if (need && !clr[a[x]]) clr[a[x]] = 1, pmx[a[x]] = Max[a[x]], pmn[a[x]] = Min[a[x]];
Max[a[x]] = max(Max[a[x]], x), Min[a[x]] = min(Min[a[x]], x);
res = max(res, Max[a[x]] - Min[a[x]]);
}
void del(int x) {
if (clr[a[x]]) {
Max[a[x]] = pmx[a[x]], Min[a[x]] = pmn[a[x]];
pmx[a[x]] = pmn[a[x]] = 0;
clr[a[x]] = 0;
}
}
int main() {
scanf("%d", &n), B = sqrt(n);
for (int i = 1; i <= n; i++) Max[i] = 0, Min[i] = INF;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i];
sort(b + 1, b + 1 + n); n1 = unique(b + 1, b + 1 + n) - b - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + 1 + n1, a[i]) - b;
scanf("%d", &q);
for (int i = 1; i <= q; i++) scanf("%d%d", &p[i].l, &p[i].r), p[i].id = i;
sort(p + 1, p + 1 + q, cmp);
int i = 1, j = 1;
while (i <= q) {
lst = res = 0;
for (int v = 1; v <= n1; v++) Max[v] = 0, Min[v] = INF;
while (j < q && (p[i].l - 1) / B == (p[j + 1].l - 1) / B) j++;
int k = i, R = ((p[i].l - 1) / B + 1) * B;
while (k <= j && p[k].r <= R) ans[p[k].id] = get(p[k].l, p[k].r), k++;
int l = R + 1, r = R;
while (k <= j) {
while (r < p[k].r) add(++r, 0);
lst = res;
while (l > p[k].l) add(--l, 1);
// cout << '\t' << l << ' ' << r << ' ' << p[k].id << endl;
ans[p[k].id] = res;
while (l < R + 1) del(l++);
res = lst;
k++;
}
i = j + 1;
}
for (int k = 1; k <= q; k++) printf("%d\n", ans[k]);
return 0;
}
歴史の研究
同理。
莫队二次离线等其它知识点
idk, idk, idk.

浙公网安备 33010602011771号