1 条题解
-
0
题解
思路概述
图中所有节点一开始互不连通,需要支持按连通块/单点/全局增量及最大值查询。若直接维护每个点的真实权值,
U/A2/A3会导致整块修改,A1又要求能够快速把某个点从所属连通块的最大值结构中删除再插入。因此可以为每个连通块维护一个左偏堆(大根),堆里保存该块内所有节点的“真实值减去本块与全局懒标记”的结果;再配合并查集维护块代表以及每块的懒加标记。val[x]:节点在去掉所有懒标记后的值。add[root]:该连通块内所有节点的附加值,表示执行A2时的懒标记。madd:全局加法标记,表示执行A3后的增量。- 每个联通块的堆根即为当前块内的最大节点,使用左偏堆可以在
O(log n)时间内完成合并和删除。 - 另用 multiset 维护所有块根的
val[root] + add[root],便于回答F3。
各操作实现
- 并查集 + 左偏堆合并 (
U):- 通过并查集找到两个块的堆根,若已在同一块则忽略。
- 将节点数少的块合并到大的块,合并前把它的
add差值 push_down 到整堆,使两个堆处于同一“基准系”。 - 左偏堆
merge过程中会始终保持堆根是块内最大值;合并完毕后更新块大小、懒标记并在 multiset 中删除旧根、插入新根。
- 单点加 (
A1):- 需要把节点从所属堆中删除、修改值后重新插入。
erase(x,v)通过沿着父指针将x脱离堆,再在val[x]上加v后调用merge把它放回堆中即可。
- 需要把节点从所属堆中删除、修改值后重新插入。
- 整块加 (
A2):- 在块代表上直接累加
add[root]+=v,并用 multiset 更新该块的最大值即可。
- 在块代表上直接累加
- 全局加 (
A3):- 累加
madd,其它结构都不需变化。
- 累加
- 查询:
F1 x:返回madd + add[find(x)] + val[x]。F2 x:所在块的最大值即堆根,答案为madd + add[root] + val[root]。F3:multiset 里储存的是所有块根的当前最大值,输出madd + *st.rbegin()。
各种堆操作都保持在
O(log n)复杂度,并查集合并同样只需均摊α(n)。因此总复杂度O((n+Q) \log n),内存O(n)。参考代码
#include <bits/stdc++.h> using namespace std; const int N = 3e5 + 5; int n, q, val[N], fa[N], ch[N][2], d[N], sz[N], f[N], add[N], madd; int& rs(int x) { return ch[x][d[ch[x][1]] < d[ch[x][0]]]; } multiset<int> st; int find(int x) { return (x == f[x]) ? x : f[x] = find(f[x]); } void push_down(int x, int v) { val[x] += v; (ch[x][0]) && (push_down(ch[x][0], v), 1); (ch[x][1]) && (push_down(ch[x][1], v), 1); } void push_up(int x) { if (!x) { return; } if (d[x] != d[rs(x)] + 1) { d[x] = d[rs(x)] + 1; push_up(fa[x]); } } int merge(int x, int y) { if (!x || !y) { return x + y; } (val[x] < val[y]) && (swap(x, y), 1); fa[rs(x) = merge(rs(x), y)] = x; push_up(x); return x; } void cle(int x) { ch[x][0] = ch[x][1] = fa[x] = 0, d[x] = 1; } void Union(int x, int y) { if ((!x) || (!y) || (x == y)) { return; } (sz[x] < sz[y]) && (swap(x, y), 1); push_down(y, add[y] - add[x]); f[x] = f[y] = merge(x, y); if (f[x] == x) { st.erase(st.find(val[y] + add[x])); sz[x] += sz[y], sz[y] = add[y] = 0; } else { st.erase(st.find(val[x] + add[x])); sz[y] += sz[x], add[y] = add[x], sz[x] = add[x] = 0; } } int erase(int x,int v) { int rt = find(x), y; if (x == rt){ fa[ch[x][0]] = fa[ch[x][1]] = 0; y = merge(ch[x][0],ch[x][1]); st.erase(st.find(val[x] + add[x])); val[x] += v,cle(x); f[x] = f[y] = merge(x,y); st.insert(val[f[x]] + add[x]); if (f[x] == y){ add[y] = add[x],sz[y] = sz[x],add[x] = sz[x] = 0; } } else{ fa[ch[x][0]] = fa[ch[x][1]] = fa[x]; ch[fa[x]][x == ch[fa[x]][1]] = merge(ch[x][0],ch[x][1]); val[x] += v,cle(x); y = find(x); f[x] = f[y] = merge(x,y); if (f[x] == x){ st.erase(st.find(val[y] + add[y])); st.insert(val[x] + add[y]); add[x] = add[y],sz[x] = sz[y],add[y] = sz[y] = 0; } } return y; } signed main() { cin >> n; for (int i = 1; i <= n; i++) { cin >> val[i], sz[i] = d[i] = 1, f[i] = i; st.insert(val[i]); } for (cin >> q; q--;) { string s; int x, y, v; cin >> s; if (s == "U") { cin >> x >> y; Union(find(x), find(y)); } if (s == "A1") { cin >> x >> v; erase(x,v); } if (s == "A2") { cin >> x >> v; x = find(x); st.erase(st.find(add[x] + val[x])); add[x] += v; st.insert(add[x] + val[x]); } if (s == "A3") { cin >> v, madd += v; } if (s == "F1") { cin >> x; cout << madd + add[find(x)] + val[x] << "\n"; } if (s == "F2") { cin >> x; cout << madd + add[find(x)] + val[find(x)] << "\n"; } if (s == "F3") { cout << madd + (*st.rbegin()) << "\n"; } } } /* 鏈€澶у€? */
- 1
信息
- ID
- 5872
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者