莫队
普通莫队
P2709 【模板】莫队 / 小 B 的询问
我们开一个桶 表示区间内某个颜色 的出现次数,直接用莫队维护即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;long long n, m, l, r, k;long long a[N], pos[N], cnt[N], now, ans[N], L[N], R[N];
struct Query { int l, r, id;}Q[N];
bool cmp(Query a, Query b) { if(pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l]; else if(pos[a.r] & 1) return pos[a.r] < pos[b.r]; else return pos[a.r] > pos[b.r];}
void add(int x) { int val = cnt[a[x]]; cnt[a[x]] ++; now = now - val * val + cnt[a[x]] * cnt[a[x]];}
void del(int x) { int val = cnt[a[x]]; cnt[a[x]] --; now = now - val * val + cnt[a[x]] * cnt[a[x]];}
int main() { cin >> n >> m >> k;
int block = sqrt(n); int num = ceil((double) n / (double) block);
for(int i = 1; i <= num; i ++) { L[i] = (i - 1) * block + 1; R[i] = i * block;
for(int j = L[i]; j <= R[i]; j ++) { pos[j] = i; } }
for(int i = 1; i <= n; i ++) { cin >> a[i]; }
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 left = 1, right = 0;
for(int i = 1; i <= m; i ++) { int l = Q[i].l, r = Q[i].r, id = Q[i].id;
while(left < l) del(left ++); while(left > l) add(-- left); while(right < r) add(++ right); while(right > r) del(right --);
ans[id] = now; }
for(int i = 1; i <= m; i ++) cout << ans[i] << endl;
return 0;}P1494 【国家集训队】 小 Z 的袜子
我们考虑用莫队维护区间内抽到相同颜色的出现概率,用桶直接维护每种颜色的出现次数即可,注意输出格式要求输出最简分数。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;long long cnt[N], answer = 0;int pos[N], a[N], n, m;pair<long long, long long> result[N];
long long gcd(long long a, long long b) { if (b == 0) return a; return gcd(b, a % b);}
struct Query { int l, r, id;
bool operator < (const Query & rhv) const { if (pos[l] == pos[rhv.l]) return r < rhv.r; return l < rhv.l; }} query[N];
void update(int pos, int add) { answer -= cnt[a[pos]] * cnt[a[pos]]; cnt[a[pos]] += add; answer += cnt[a[pos]] * cnt[a[pos]];}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m;
int block = sqrt(n);
for (int i = 1; i <= n; i++) { cin >> a[i]; pos[i] = (i - 1) / block + 1; }
for (int i = 1; i <= m; i++) { cin >> query[i].l >> query[i].r; query[i].id = i; }
sort(query + 1, query + m + 1);
int L = 1, R = 0;
for (int i = 1; i <= m; i++) { while (L < query[i].l) update(L, -1), L++; while (L > query[i].l) L--, update(L, 1); while (R < query[i].r) R++, update(R, 1); while (R > query[i].r) update(R, -1), R--;
if (query[i].l == query[i].r) { result[query[i].id] = make_pair(0, 1); continue; }
long long a = answer - (query[i].r - query[i].l + 1); long long b = (long long) (query[i].r - query[i].l + 1) * (query[i].r - query[i].l); long long gcd_value = gcd(a, b);
result[query[i].id] = make_pair(a / gcd_value, b / gcd_value); }
for (int i = 1; i <= m; i ++) { cout << result[i].first << "/" << result[i].second << '\n'; }
return 0;}P4462 【CQOI2018】 异或序列
我们定义 ,询问我们有多少组 满足 即为 ,开桶统计即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int;using i64 = long long;using i128 = __int128;using ui64 = unsigned long long;using f32 = float;using f64 = double;
constexpr i32 N = 1e5 + 5;
i32 n, m, a[N], k;i32 l[N], r[N], pos[N], block, num;i64 ans[N], sum[N], cur;vector<tuple<i32, i32, i32>> queries;
inline void Add(i32 i) { cur += sum[a[i] ^ k]; sum[a[i]]++;}
inline void Del(i32 i) { sum[a[i]]--; cur -= sum[a[i] ^ k];}
i32 main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m >> k;
for (i32 i = 1; i <= n; i++) { cin >> a[i]; a[i] ^= a[i - 1]; }
queries.resize(m); for (i32 i = 0; i < m; i++) { cin >> get<0>(queries[i]) >> get<1>(queries[i]); get<2>(queries[i]) = i; }
block = sqrt(n); num = (n + block - 1) / block;
for (i32 i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = i * block; for (i32 j = l[i]; j <= r[i]; j++) { pos[j] = i; } }
sort(queries.begin(), queries.end(), [&] (auto a, auto b) { if (pos[get<0>(a)] != pos[get<0>(b)]) return pos[get<0>(a)] < pos[get<0>(b)]; else if (pos[get<0>(a)] & 1) return get<1>(a) > get<1>(b); else return get<1>(a) < get<1>(b); });
i32 L = 0, R = 0; sum[0] = 1; for (i32 i = 0; i < (i32) queries.size(); i++) { while (L < get<0>(queries[i]) - 1) Del(L++); while (L > get<0>(queries[i]) - 1) Add(--L); while (R < get<1>(queries[i])) Add(++R); while (R > get<1>(queries[i])) Del(R--);
ans[get<2>(queries[i])] = cur; }
for (i32 i = 0; i < m; i++) { cout << ans[i] << "\n"; }
return 0;}P3709 大爷的字符串题
设 表示当前区间内 这个数字的出现次数,不难发现,rp 的最终结果只和区间内最大的 有关,莫队维护即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int;using i64 = long long;using f32 = float;using f64 = double;
constexpr i32 N = 2e5 + 5;
i32 n, m, a[N], ans[N];i32 l[N], r[N], pos[N], block, num;i32 tot[N], refer[N], cur = 0;vector<tuple<i32, i32, i32>> queries;vector<int> vars;
inline void Add(i32 i) { refer[tot[a[i]]]--; tot[a[i]]++; refer[tot[a[i]]]++; cur = max(cur, tot[a[i]]);}
inline void Del(i32 i) { refer[tot[a[i]]]--; if (cur == tot[a[i]] && refer[tot[a[i]]] == 0) cur--; tot[a[i]]--; refer[tot[a[i]]]++;}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m;
for (i32 i = 1; i <= n; i++) { cin >> a[i]; vars.push_back(a[i]); }
sort(vars.begin(), vars.end()); vars.erase(unique(vars.begin(), vars.end()), vars.end());
for (i32 i = 1; i <= n; i++) { a[i] = (i32) (lower_bound(vars.begin(), vars.end(), a[i]) - vars.begin() + 1); }
queries.resize(m); for (i32 i = 0; i < m; i++) { cin >> get<0>(queries[i]) >> get<1>(queries[i]); get<2>(queries[i]) = i; }
block = sqrt(n); num = (n + block - 1) / block;
for (i32 i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = min(n, i * block);
for (int j = l[i]; j <= r[i]; j++) pos[j] = i; }
sort(queries.begin(), queries.end(), [&] (auto a, auto b) { if (pos[get<0>(a)] != pos[get<0>(b)]) return pos[get<0>(a)] < pos[get<0>(b)]; if (pos[get<0>(a)] & 1) return get<1>(a) < get<1>(b); return get<1>(a) > get<1>(b); });
i32 L = 0, R = 0; refer[0] = n; for (i32 i = 0; i < (i32) queries.size(); i++) { while (L < get<0>(queries[i])) Del(L++); while (L > get<0>(queries[i])) Add(--L); while (R < get<1>(queries[i])) Add(++R); while (R > get<1>(queries[i])) Del(R--);
ans[get<2>(queries[i])] = -cur; }
for (i32 i = 0; i < m; i++) { cout << ans[i] << "\n"; }
return 0;}带修莫队
带修莫队就是在普通莫队的基础上加上了一个时间维度,我们只要记录查询的时间,并将之前的操作全部应用即可,注意这时候块长取 可以取到最优复杂度。
P1903 【模板】带修莫队 / 【国家集训队】 数颜色 / 维护队列
我们维护一个时间维度上的修改操作即可。
#include <bits/stdc++.h>
#ifdef LOCAL#include <algo/debug.h>#else#define debug(...) 42#endif
using namespace std;
constexpr int N = 6e5 + 5;
int n, m, c[N], cpy[N], lst[N], answer[N];int cnt_queries = 0;set<int> positions[N];vector<int> vars;
int cnt_op = 0;struct Op { int type, x, r, y, val, index;} ops[N];
struct Fenwick { int tr[N], Limit = 0; Fenwick() = default; Fenwick(int n) : Limit(n) { fill(tr, tr + Limit + 1, 0); } static int Lowbit(int x) { return x & (-x); } void Modify(int pos, int var) { for (int i = pos; i <= Limit; i += Lowbit(i)) tr[i] += var; } int Query(int pos) { int retval = 0; for (int i = pos; i; i -= Lowbit(i)) retval += tr[i]; return retval; }} tr;
int GetColor(int col) { return (int) (lower_bound(vars.begin(), vars.end(), col) - vars.begin() + 1);}
void CDQ(int l, int r) { if (l == r) return;
int mid = (l + r) >> 1; CDQ(l, mid); CDQ(mid + 1, r);
sort(ops + l, ops + mid + 1, [&] (auto a, auto b) { if (a.y != b.y) return a.y < b.y; return a.x < b.x; }); sort(ops + mid + 1, ops + r + 1, [&] (auto a, auto b) { if (a.y != b.y) return a.y < b.y; return a.x < b.x; });
int pl = l, pr = mid + 1; while (pr <= r) { while (pl <= mid && ops[pl].y < ops[pr].y) { if (ops[pl].type == 1) { tr.Modify(ops[pl].x, ops[pl].val); } pl++; }
if (ops[pr].type == 2) { answer[ops[pr].index] += tr.Query(ops[pr].r) - tr.Query(ops[pr].x - 1); } pr++; }
for (int i = l; i < pl; i++) { if (ops[i].type == 1) { tr.Modify(ops[i].x, -ops[i].val); } }}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) { cin >> c[i]; cpy[i] = c[i]; vars.push_back(c[i]); }
vector<tuple<char, int, int>> raw(m + 1); for (int i = 1; i <= m; i++) { auto&& [x, y, z] = raw[i]; cin >> x >> y >> z;
if (x == 'R') { vars.push_back(z); } }
sort(vars.begin(), vars.end()); vars.erase(unique(vars.begin(), vars.end()), vars.end());
for (int i = 1; i <= n; i++) { positions[GetColor(c[i])].insert(i); }
for (int i = 1; i <= n; i++) { int cur_col = GetColor(c[i]); ops[++cnt_op] = Op{1, i, 0, lst[cur_col], 1, 0}; lst[cur_col] = i; }
for (int i = 1; i <= m; i++) { if (get<0>(raw[i]) == 'Q') { auto&& [op, l, r] = raw[i];
ops[++cnt_op] = Op{2, l, r, l, 0, ++cnt_queries}; } else if (get<0>(raw[i]) == 'R') { auto&& [op, pos, nxt_col] = raw[i]; int cur_col = cpy[pos];
auto Current = positions[GetColor(cur_col)].find(pos); int pre = 0, suc = 0; if (Current != positions[GetColor(cur_col)].begin()) { pre = *prev(Current); } if (next(Current) != positions[GetColor(cur_col)].end()) { suc = *next(Current); }
ops[++cnt_op] = Op{1, pos, 0, pre, -1, 0};
if (suc != 0) { ops[++cnt_op] = Op{1, suc, 0, pos, -1, 0}; ops[++cnt_op] = Op{1, suc, 0, pre, 1, 0}; } positions[GetColor(cur_col)].erase(Current);
Current = positions[GetColor(nxt_col)].lower_bound(pos); pre = 0, suc = 0; if (Current != positions[GetColor(nxt_col)].begin()) { pre = *prev(Current); } if (Current != positions[GetColor(nxt_col)].end()) { suc = *Current; }
ops[++cnt_op] = Op{1, pos, 0, pre, 1, 0};
if (suc != 0) { ops[++cnt_op] = Op{1, suc, 0, pre, -1, 0}; ops[++cnt_op] = Op{1, suc, 0, pos, 1, 0}; } positions[GetColor(nxt_col)].insert(pos);
cpy[pos] = nxt_col; } }
tr = Fenwick(n); CDQ(1, cnt_op);
for (int i = 1; i <= cnt_queries; i++) { cout << answer[i] << "\n"; }
return 0;}Problem - 940F - Codeforces
直接开桶暴力维护mex即可。
#include <bits/stdc++.h>
using namespace std;
using i32 = int;using i64 = long long;using f32 = float;using f64 = double;
constexpr i32 N = 5e5 + 5;
i32 n, q, a[N], ans[N];i32 l[N], r[N], pos[N], block, num;vector<tuple<i32, i32, i32>> query, modify;vector<i32> vars;i32 cnt[N], tot[N];
inline void Add(i32 val) { if (cnt[val] > 0) tot[cnt[val]]--; cnt[val]++; if (cnt[val] > 0) tot[cnt[val]]++;}
inline void Del(i32 val) { if (cnt[val] > 0) tot[cnt[val]]--; cnt[val]--; if (cnt[val] > 0) tot[cnt[val]]++;}
inline void Upd(i32 idx_q, i32 idx_m) { if (get<0>(modify[idx_m]) >= get<0>(query[idx_q]) && get<0>(modify[idx_m]) <= get<1>(query[idx_q])) { Del(a[get<0>(modify[idx_m])]); Add(get<1>(modify[idx_m])); } swap(get<1>(modify[idx_m]), a[get<0>(modify[idx_m])]);}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> q;
for (i32 i = 1; i <= n; i++) { cin >> a[i]; vars.push_back(a[i]); }
for (i32 i = 1; i <= q; i++) { i32 op, l, r; cin >> op >> l >> r;
if (op == 1) { query.push_back(make_tuple(l, r, i)); } else { modify.push_back(make_tuple(l, r, i)); vars.push_back(r); } }
sort(vars.begin(), vars.end()); vars.erase(unique(vars.begin(), vars.end()), vars.end());
for (i32 i = 1; i <= n; i++) { a[i] = lower_bound(vars.begin(), vars.end(), a[i]) - vars.begin() + 1; } for (i32 i = 0; i < (i32) modify.size(); i++) { get<1>(modify[i]) = lower_bound(vars.begin(), vars.end(), get<1>(modify[i])) - vars.begin() + 1; }
block = pow(n, 2.0 / 3); num = (n + block - 1) / block;
for (i32 i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = min(i * block, n);
for (i32 j = l[i]; j <= r[i]; j++) pos[j] = i; }
sort(query.begin(), query.end(), [&] (auto a, auto b) { if (pos[get<0>(a)] != pos[get<0>(b)]) return pos[get<0>(a)] < pos[get<0>(b)]; if (pos[get<1>(a)] != pos[get<1>(b)]) return pos[get<1>(a)] < pos[get<1>(b)]; return get<2>(a) < get<2>(b); }); sort(modify.begin(), modify.end(), [&] (auto a, auto b) { return get<2>(a) < get<2>(b); });
i32 L = 0, R = 0, idx = -1; for (i32 i = 0; i < (i32) query.size(); i++) { while (L < get<0>(query[i])) Del(a[L++]); while (L > get<0>(query[i])) Add(a[--L]); while (R < get<1>(query[i])) Add(a[++R]); while (R > get<1>(query[i])) Del(a[R--]); while (idx + 1 < (i32) modify.size() && get<2>(modify[idx + 1]) < get<2>(query[i])) Upd(i, ++idx); while (idx >= 0 && get<2>(modify[idx]) > get<2>(query[i])) Upd(i, idx--);
ans[get<2>(query[i])] = 1; while (tot[ans[get<2>(query[i])]] > 0) ans[get<2>(query[i])]++; }
for (int i = 1; i <= q; i++) { if (ans[i]) cout << ans[i] << "\n"; }
return 0;}回滚莫队
对于一些加入元素很难,但是删除元素很简单的题目,我们可以考虑只维护插入的元素,然后处理完当前询问后直接继承之前的状态即可,这样可以保证复杂度为 。反之亦然。
P5906 【模板】回滚莫队&不删除莫队
板子题。
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 4e5 + 5;
int n, m, a[N];int l[N], r[N], pos[N], block, num;int max_pos[N], min_pos[N], _max_pos[N];int max_dist, ans[N];vector<tuple<int, int, int>> queries;vector<int> vars;
inline int BruteQuery(int l, int r) { static int mx[N], mn[N]; int res = 0; for (int i = l; i <= r; i++) { if (!mn[a[i]]) mn[a[i]] = i; mx[a[i]] = i; res = max(res, mx[a[i]] - mn[a[i]]); }
for (int i = l; i <= r; i++) mx[a[i]] = mn[a[i]] = 0;
return res;}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) { cin >> a[i]; vars.push_back(a[i]); }
sort(vars.begin(), vars.end()); vars.erase(unique(vars.begin(), vars.end()), vars.end());
for (int i = 1; i <= n; i++) { a[i] = lower_bound(vars.begin(), vars.end(), a[i]) - vars.begin() + 1; }
cin >> m; for (int i = 1; i <= m; i++) { int left, right; cin >> left >> right; queries.push_back(make_tuple(left, right, i)); }
block = sqrt(n); num = (n + block - 1) / block;
for (int i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = min(i * block, n);
for (int j = l[i]; j <= r[i]; j++) pos[j] = i; }
sort(queries.begin(), queries.end(), [&] (auto a, auto b) { if (pos[get<0>(a)] != pos[get<0>(b)]) return pos[get<0>(a)] < pos[get<0>(b)]; return get<1>(a) < get<1>(b); });
int query_idx = 0; for (int block_idx = 1; block_idx <= num; block_idx++) { max_dist = 0; int L = r[block_idx] + 1, R = r[block_idx]; memset(max_pos, 0, sizeof(max_pos)); memset(min_pos, 0, sizeof(min_pos));
while (query_idx < (int) queries.size() && pos[get<0>(queries[query_idx])] == block_idx) { auto&& [ql, qr, idx] = queries[query_idx];
if (qr <= r[block_idx]) { ans[idx] = BruteQuery(ql, qr); } else { while (R < qr) { R++; if (!min_pos[a[R]]) min_pos[a[R]] = R; max_pos[a[R]] = R; max_dist = max(max_dist, max_pos[a[R]] - min_pos[a[R]]); }
int _max_dist = 0, _L = L; while (L > ql) { L--; if (!_max_pos[a[L]]) _max_pos[a[L]] = L; _max_dist = max(_max_dist, max(_max_pos[a[L]], max_pos[a[L]]) - L); }
ans[idx] = max(_max_dist, max_dist);
while (L < _L) _max_pos[a[L++]] = 0; }
query_idx++; } }
for (int i = 1; i <= m; i++) { cout << ans[i] << "\n"; }
return 0;}P14420 【JOISC 2014】 历史的研究 / Historical Research
容易发现删除的时候最大值不连续,所以删除不太好维护,我们考虑使用不删除莫队维护,每次回滚状态即可。
#include <bits/stdc++.h>
#ifdef LOCAL#include <algo/debug.h>#else#define debug(...) 42#endif
using namespace std;
using i32 = int;using i64 = long long;using f32 = float;using f64 = double;
constexpr i32 N = 3e5 + 5;
i32 n, q, x[N];i32 l[N], r[N], pos[N], block, num;i64 cnt[N], ans[N], cur_max = 0;vector<tuple<i32, i32, i32>> queries;vector<i32> vars;
inline void Add(i32 val) { cnt[val]++; cur_max = max(cur_max, (i64) vars[val - 1] * cnt[val]);}
inline void Del(i32 val) { cnt[val]--;}
inline i64 BruteQuery(i32 L, i32 R) { i64 max_var = 0; map<i32, i32> cnt; for (i32 i = L; i <= R; i++) { cnt[x[i]]++; max_var = max(max_var, (i64) vars[x[i] - 1] * cnt[x[i]]); } return max_var;}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> q;
for (i32 i = 1; i <= n; i++) { cin >> x[i]; vars.push_back(x[i]); }
for (i32 i = 1; i <= q; i++) { i32 left, right; cin >> left >> right; queries.push_back(make_tuple(left, right, i)); }
sort(vars.begin(), vars.end()); vars.erase(unique(vars.begin(), vars.end()), vars.end());
for (i32 i = 1; i <= n; i++) { x[i] = lower_bound(vars.begin(), vars.end(), x[i]) - vars.begin() + 1; }
block = sqrt(n); num = (n + block - 1) / block;
for (i32 i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = min(n, i * block);
for (i32 j = l[i]; j <= r[i]; j++) pos[j] = i; }
for (i32 i = 1; i <= n; i++) debug(pos[i]);
sort(queries.begin(), queries.end(), [&] (auto a, auto b) { if (pos[get<0>(a)] != pos[get<0>(b)]) return pos[get<0>(a)] < pos[get<0>(b)]; return get<1>(a) < get<1>(b); });
i32 query_idx = 0; for (i32 block_idx = 1; block_idx <= num; block_idx++) { cur_max = 0; i32 L = r[block_idx] + 1, R = r[block_idx];
fill(cnt, cnt + (i32) vars.size() + 5, 0);
while (query_idx < (i32) queries.size() && pos[get<0>(queries[query_idx])] == block_idx) { debug(L, R, cur_max, query_idx);
auto&& [ql, qr, idx] = queries[query_idx];
if (qr <= r[block_idx]) { ans[idx] = BruteQuery(ql, qr); } else { while (R < qr) Add(x[++R]); i64 _cur_max = cur_max; while (L > ql) Add(x[--L]);
ans[idx] = cur_max;
while (L <= r[block_idx]) Del(x[L++]); cur_max = _cur_max; }
query_idx++; } }
for (i32 i = 1; i <= q; i++) { cout << ans[i] << "\n"; }
return 0;}P8078 【WC2022】 秃子酋长
发现删除好做加入难,使用回滚莫队维护。用链表维护当前的序列即可。
#include <bits/stdc++.h>
using namespace std;
namespace IO { template<class _tp> inline void read(_tp& x) { x = 0; _tp f = 1; char ch = getchar_unlocked(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar_unlocked(); } while (isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar_unlocked(); } x *= f; } template<class _tp, class... args> inline void read(_tp& x, args& ...t) { read(x); read(t...); } template<class _tp> inline void _write(_tp x) { if (x < 0) putchar_unlocked('-'), x = -x; if (x > 9) _write(x / 10); putchar_unlocked(x % 10 + '0'); } template<class _tp> inline void write(_tp x, char split = '\n') { _write(x); putchar(split); }} using namespace IO;
constexpr int N = 5e5 + 5;
int n, m, a[N], refer[N];int pos[N], l[N], r[N], block, num;long long ans[N];
struct Query { int l, r, idx; Query() = default; Query(int l, int r, int i) : l(l), r(r), idx(i) {} bool operator<(const Query& rhs) const { if (pos[l] != pos[rhs.l]) return pos[l] < pos[rhs.l]; return r > rhs.r; }} query[N];
stack<int> stk;
struct Link_t { int _prev, _next; Link_t() = default; Link_t(int l, int r) : _prev(l), _next(r) {} inline int& prev() { return _prev; } inline int& next() { return _next; }} Link[N];
inline long long Del(int p) { long long cur = 0; if (Link[p].prev() != 0 && Link[p].next() != 0) cur += abs(refer[Link[p].prev()] - refer[Link[p].next()]); if (Link[p].prev() != 0) cur -= abs(refer[p] - refer[Link[p].prev()]); if (Link[p].next() != 0) cur -= abs(refer[p] - refer[Link[p].next()]);
stk.push(p); Link[Link[p].prev()].next() = Link[p].next(); Link[Link[p].next()].prev() = Link[p].prev();
return cur;}
inline void Undo() { int p = stk.top(); stk.pop(); Link[Link[p].prev()].next() = p; Link[Link[p].next()].prev() = p;}
int main() { // cin >> n >> m; read(n, m);
for (int i = 1; i <= n; i++) { // cin >> a[i]; read(a[i]); refer[a[i]] = i; }
for (int i = 1; i <= n; i++) { Link[i].prev() = i - 1; Link[i].next() = i + 1; } Link[n].next() = 0;
for (int i = 1; i <= m; i++) { // cin >> query[i].l >> query[i].r; read(query[i].l, query[i].r); query[i].idx = i; }
block = sqrt(n); num = (n + block - 1) / block;
for (int i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = min(n, i * block);
for (int j = l[i]; j <= r[i]; j++) pos[j] = i; }
sort(query + 1, query + m + 1);
int L = 1, R = n, lst_block = 1; long long tmp = 0, cur = 0;
for (int i = 2; i <= n; i++) { cur += abs(refer[i] - refer[i - 1]); }
tmp = cur;
for (int i = 1; i <= m; i++) { auto&& [ql, qr, idx] = query[i];
if (pos[ql] != lst_block) { while (!stk.empty()) Undo(); while (L < l[pos[ql]]) cur += Del(a[L++]); tmp = cur; R = n; lst_block = pos[ql]; while (!stk.empty()) stk.pop(); }
while (R > qr) tmp += Del(a[R--]);
long long _tmp = tmp, _L = L; while (_L < ql) _tmp += Del(a[_L++]); while (_L > L) Undo(), _L--;
ans[idx] = _tmp; }
for (int i = 1; i <= m; i++) { // cout << ans[i] << "\n"; write(ans[i]); }
return 0;}树上莫队
我们维护一个括号序列,在 dfs 的时候每次访问和回溯都加入到括号序列当中,定义 和 表示 节点进入和退出 dfs 时在括号序列的位置,每次查询 分为两种情况:
-
直链:对应的括号序列的区间为 ,中间若同一个节点出现两次,则正负抵消,因为这样的节点一定不在 的路径上。
-
折链:用树剖的思想,将 转化为 和 ,这样就得到了两条直链,直接维护即可。
P4074 【WC2013】 糖果公园
模板题。
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 5e5 + 5;constexpr int K = 25;
int n, m, q, v[N], w[N], c[N], lst[N];int l[N], r[N], pos[N], block, num;int seq[N], refer[N], in[N], out[N], cnt = 0;int jmp[N][K], dep[N];int f[N], tot[N];long long cur, ans[N];vector<int> adj[N];vector<tuple<int, int, int, int>> query, modify;
inline void Dfs(int u, int fa) { dep[u] = dep[fa] + 1; seq[++cnt] = u; in[u] = cnt; jmp[u][0] = fa;
for (int k = 1; k < K; k++) jmp[u][k] = jmp[jmp[u][k - 1]][k - 1];
for (auto v : adj[u]) { if (v == fa) continue; Dfs(v, u); }
seq[++cnt] = u; out[u] = cnt;}
inline int GetLCA(int u, int v) { if (dep[u] > dep[v]) swap(u, v);
for (int k = K - 1; k >= 0; k--) { if (dep[jmp[v][k]] >= dep[u]) v = jmp[v][k]; }
if (u == v) return u;
for (int k = K - 1; k >= 0; k--) { if (jmp[u][k] != jmp[v][k]) tie(u, v) = tie(jmp[u][k], jmp[v][k]); }
return jmp[u][0];}
inline void Chg(int pos) { if (f[pos]) { cur -= (long long) v[c[pos]] * w[tot[c[pos]]]; tot[c[pos]]--; } else { tot[c[pos]]++; cur += (long long) v[c[pos]] * w[tot[c[pos]]]; } f[pos] ^= 1;}
inline void Upd(int x, int t) { if (f[x]) { Chg(x); c[x] = t; Chg(x); } else { c[x] = t; }}
signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
// freopen("P4074_4.in", "r", stdin);
cin >> n >> m >> q;
for (int i = 1; i <= m; i++) { cin >> v[i]; } for (int i = 1; i <= n; i++) { cin >> w[i]; }
for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); }
for (int i = 1; i <= n; i++) { cin >> c[i]; lst[i] = c[i]; }
Dfs(1, 0);
memset(ans, -1, sizeof(ans)); for (int i = 1; i <= q; i++) { int typ, x, y; cin >> typ >> x >> y;
if (typ == 0) { modify.push_back(tuple(x, lst[x], y, i)); lst[x] = y; } else if (typ == 1) { if (in[x] > in[y]) swap(x, y); query.push_back(tuple(GetLCA(x, y) == x ? in[x] : out[x], in[y], i, i)); } }
block = pow(2 * n, 2.0 / 3); num = (2 * n + block - 1) / block;
for (int i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = min(2 * n, i * block);
for (int j = l[i]; j <= r[i]; j++) pos[j] = i; }
sort(query.begin(), query.end(), [&] (auto a, auto b) { if (pos[get<0>(a)] != pos[get<0>(b)]) return pos[get<0>(a)] < pos[get<0>(b)]; if (pos[get<1>(a)] != pos[get<1>(b)]) return pos[get<1>(a)] < pos[get<1>(b)]; return get<2>(a) < get<2>(b); }); // sort(modify.begin(), modify.end(), [&] (auto a, auto b) { // return get<3>(a) < get<3>(b); // });
int L = 1, R = 0, T = -1;
for (int i = 0; i < (int) query.size(); i++) { auto&& [ql, qr, idx, _] = query[i];
while (!modify.empty() && T + 1 < (int) modify.size() && get<3>(modify[T + 1]) < idx) { T++; Upd(get<0>(modify[T]), get<2>(modify[T])); } while (!modify.empty() && T >= 0 && get<3>(modify[T]) > idx) { Upd(get<0>(modify[T]), get<1>(modify[T])); T--; }
while (L > ql) Chg(seq[--L]); while (L < ql) Chg(seq[L++]); while (R > qr) Chg(seq[R--]); while (R < qr) Chg(seq[++R]);
int x = seq[ql], y = seq[qr]; int LCA = GetLCA(x, y);
if (x != LCA && y != LCA) { Chg(LCA); ans[idx] = cur; Chg(LCA); } else { ans[idx] = cur; } }
for (int i = 1; i <= q; i++) { if (ans[i] == -1) continue; cout << ans[i] << "\n"; }
return 0;}P4689 【Ynoi Easy Round 2016】 这是我自己的发明 - 洛谷
用类似 P5268 的方式将映射在括号序上的询问拆分一下即可。
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 1e5 + 5;constexpr int K = 25;constexpr int M = 5e5 + 5;
int n, m, a[N];int l[N], r[N], pos[N], block, num;int dfn[N], t = 0, rf[N], dep[N], siz[N];int jmp[N][K];int cl[N], cr[N];long long ans[M], cur;vector<int> vars, adj[N];
struct Query { int ql, qr, f, idx; bool operator<(const Query& rhs) const { if (pos[ql] != pos[rhs.ql]) return pos[ql] < pos[rhs.ql]; return qr < rhs.qr; }};vector<Query> query;
inline void Dfs(int u, int father) { dep[u] = dep[father] + 1; jmp[u][0] = father; siz[u] = 1; dfn[u] = ++t; for (int k = 1; k < K; k++) jmp[u][k] = jmp[jmp[u][k - 1]][k - 1];
for (auto&& v : adj[u]) { if (v == father) continue; Dfs(v, u); siz[u] += siz[v]; }}
inline int GetKth(int u, int kth) { for (int k = K - 1; k >= 0; k--) { if (kth >> k & 1) u = jmp[u][k]; } return u;}
inline vector<pair<int, int>> GetRanges(int u, int root) { vector<pair<int, int>> ranges; if (u == root) { ranges.push_back(pair(1, t)); } else if (dfn[root] >= dfn[u] && dfn[root] <= dfn[u] + siz[u] - 1) { int kth = GetKth(root, dep[root] - dep[u] - 1); if (dfn[kth] - 1 >= 1) ranges.push_back(pair(1, dfn[kth] - 1)); if (dfn[kth] + siz[kth] <= t) ranges.push_back(pair(dfn[kth] + siz[kth], t)); } else { ranges.push_back(pair(dfn[u], dfn[u] + siz[u] - 1)); }
return ranges;}
inline void UpdL(int var, int f) { cur += f * cr[var]; cl[var] += f;}
inline void UpdR(int var, int f) { cur += f * cl[var]; cr[var] += f;}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) { cin >> a[i]; vars.push_back(a[i]); }
sort(vars.begin(), vars.end()); vars.erase(unique(vars.begin(), vars.end()), vars.end());
for (int i = 1, u, v; i < n; i++) { cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); }
Dfs(1, 0);
for (int i = 1; i <= n; i++) { rf[dfn[i]] = (int) (lower_bound(vars.begin(), vars.end(), a[i]) - vars.begin() + 1); }
memset(ans, -1, sizeof(ans));
int root = 1; for (int i = 1; i <= m; i++) { int tp, x, y; cin >> tp;
if (tp == 1) { cin >> x; root = x; } else { cin >> x >> y; auto a = GetRanges(x, root), b = GetRanges(y, root);
ans[i] = 0; for (auto&& va : a) { for (auto&& vb : b) { query.push_back({va.second, vb.second, 1, i}); query.push_back({va.first - 1, vb.second, -1, i}); query.push_back({va.second, vb.first - 1, -1, i}); query.push_back({va.first - 1, vb.first - 1, 1, i}); } } } }
for (auto&& [ql, qr, f, idx] : query) { if (ql > qr) swap(ql, qr); }
block = sqrt(n); num = (n + block - 1) / block;
for (int i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = min(t, i * block);
for (int j = l[i]; j <= r[i]; j++) pos[j] = i; }
sort(query.begin(), query.end());
int L = 0, R = 0; for (auto&& [ql, qr, f, idx] : query) { while (L < ql) UpdL(rf[++L], 1); while (L > ql) UpdL(rf[L--], -1); while (R < qr) UpdR(rf[++R], 1); while (R > qr) UpdR(rf[R--], -1);
ans[idx] += cur * f; }
for (int i = 1; i <= m; i++) { if (ans[i] == -1) continue; cout << ans[i] << "\n"; }
return 0;}P5268 【SNOI2017】 一个简单的询问
容易发现 ,直接拆式子分两段维护即可。注意莫队双指针的移动方式。
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 5e4 + 5;
int n, q, a[N];int l[N], r[N], pos[N], block, num;int cl[N], cr[N];long long ans[N], cur;vector<tuple<int, int, int, int>> query;
inline void UpdL(int val, int f) { cur += f * cr[val]; cl[val] += f;}
inline void UpdR(int val, int f) { cur += f * cl[val]; cr[val] += f;}
int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) { cin >> a[i]; }
cin >> q; for (int i = 1; i <= q; i++) { int l1, r1, l2, r2; cin >> l1 >> r1 >> l2 >> r2;
query.push_back(tuple(r1, r2, 1, i)); query.push_back(tuple(l1 - 1, r2, -1, i)); query.push_back(tuple(r1, l2 - 1, -1, i)); query.push_back(tuple(l1 - 1, l2 - 1, 1, i)); }
for (auto&& [ql, qr, f, idx] : query) { if (ql > qr) swap(ql, qr); }
block = sqrt(n); num = (n + block - 1) / block;
for (int i = 1; i <= num; i++) { l[i] = (i - 1) * block + 1; r[i] = i * block;
for (int j = l[i]; j <= r[i]; j++) pos[j] = i; }
sort(query.begin(), query.end(), [&] (auto a, auto b) -> bool { if (pos[get<0>(a)] != pos[get<0>(b)]) return pos[get<0>(a)] < pos[get<0>(b)]; if (pos[get<0>(a)] & 1) return get<1>(a) < get<1>(b); return get<1>(a) > get<1>(b); });
int L = 0, R = 0; for (auto&& [ql, qr, f, idx] : query) { while (L < ql) UpdL(a[++L], 1); while (L > ql) UpdL(a[L--], -1); while (R < qr) UpdR(a[++R], 1); while (R > qr) UpdR(a[R--], -1);
ans[idx] += f * cur; }
for (int i = 1; i <= q; i++) { cout << ans[i] << "\n"; }
return 0;}
Comments
Quiet notes for this article.