(休闲娱乐)如果忘了平衡树怎么写
WorldMachine · · 休闲·娱乐
鸣谢:@MornStar,算是参考文章的一点拓展。
假如我们现在忘了平衡树怎么打,但需要 A 掉平衡树板子题,咋办。
考虑二进制分组,具体地说,维护
假如我们已经能维护插入,那么对于可差分信息,像可删堆那样再开一个相同的数据结构维护已删除的数即可。
查询
查询第
如果没有删除,那么查询
现在考虑如何支持插入。一开始新开一个只包含待插入数
每个数至多只会被归并
:::success[代码实现]{open}
#include <bits/stdc++.h>
using namespace std;
#define il inline
typedef basic_string<int> vec;
constexpr int N = 1e5 + 5, L = 17;
struct DS {
vec a[L];
il void insert(int x) {
vec v(1, x);
for (int i = 0; i < L; i++) {
if (a[i].empty()) return a[i] = v, void(); int s = v.size();
v += a[i], inplace_merge(v.begin(), v.begin() + s, v.end()), vec().swap(a[i]);
}
}
il int qrylower(int x) {
int ret = 0;
for (int i = 0; i < L; i++) ret += lower_bound(a[i].begin(), a[i].end(), x) - a[i].begin();
return ret;
}
il int qryupper(int x) {
int ret = 0;
for (int i = 0; i < L; i++) ret += upper_bound(a[i].begin(), a[i].end(), x) - a[i].begin();
return ret;
}
il int count(int x) { return qryupper(x) - qrylower(x); }
};
struct MStree {
DS a, b; int mx = INT_MIN, mn = INT_MAX;
il void insert(int x) { a.insert(x), mx = max(mx, x), mn = min(mn, x); }
il void erase(int x) { b.insert(x); }
il int rank(int x) { return a.qrylower(x) - b.qrylower(x) + 1; }
il int count(int x) { return a.count(x) - b.count(x); }
il int kth(int k) {
int L = mn, R = mx, mid, ret;
while (L <= R) rank(mid = L + R >> 1) <= k ? (ret = mid, L = mid + 1) : (R = mid - 1);
return ret;
}
il int prev(int x) { return kth(rank(x) - 1); }
il int next(int x) { return kth(a.qryupper(x) - b.qryupper(x) + 1); }
} t;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n; cin >> n;
while (n--) {
int op, x; cin >> op >> x;
if (op == 1) t.insert(x);
else if (op == 2) t.erase(x);
else if (op == 3) cout << t.rank(x) << '\n';
else if (op == 4) cout << t.kth(x) << '\n';
else if (op == 5) cout << t.prev(x) << '\n';
else cout << t.next(x) << '\n';
}
}
:::
总用时
考虑平衡复杂度,改为
具体地说,插入时:
- 若当前序列
a_i 为空,则将v 放上去,结束; - 否则若
a_i 和v 合并后大小小于k^{i+1} 则直接把v 合并到a_i 上去,结束; - 否则把
a_i 合并到v 上去,继续后面的过程。
这样每个数会被合并
实测
:::success[代码实现]{open}
constexpr int N = 1e5 + 5, L = 5, pw[] = {1, 10, 100, 1000, 10000, 100000};
// 其余部分均相同
il void insert(int x) {
vec v(1, x);
for (int i = 0; i < L; i++) {
if (a[i].empty()) return a[i] = v, void(); int s = v.size();
if (a[i].size() + s == pw[i + 1]) v += a[i], inplace_merge(v.begin(), v.begin() + s, v.end()), vec().swap(a[i]);
else return a[i] += v, inplace_merge(a[i].begin(), a[i].end() - s, a[i].end()), void();
}
}
:::
总用时
事实证明让这个东西支持删除还是太为难它了,下面考虑只有插入的情况。
这样的话取
下面开始魔怔。
考虑用这个东西来维护一些别样的信息,例如线性基这种合并复杂度比加入元素复杂度高的信息。
设加入元素复杂度为
- 把序列按照
B 为块长分块,对整块建立猫树,这样时间复杂度为\mathcal O\left(\dfrac{\text{size}\log\text{size}\cdot f}{B}\right) 。
这样插入的总复杂度为
随后每次询问一个值域区间
稍微平衡一下,发现
比如说用这个东西来维护在序列中一个位置插入一个数、查询区间线性基,可以做到