笔记汇总
定义结构体
struct Node
{
int s[2], p, v;
int rev, same; // 懒标记,一被标记就要负责(优先于查询)
int size, sum, ms, ls, rs;
void init(int _v, int _p)
{
s[0] = s[1] = 0, p = _p, v = _v;
rev = same = 0;
size = 1; sum = ms = v;
ls = rs = max(ms, 0);
}
}tr[N];
哨兵
insert(-INF), insert(INF); // 解决边界问题的常用技巧
w[0] = -INF, w[n+1] = INF; // 另一种方法
tr[0].ms = XXX; // 处理子树为空时的信息传递
伸展
void rotate(int x)
{
int y = tr[x].p, z = tr[y].p;
int k = tr[y].s[1] == x;
tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;
tr[y].s[k] = tr[x].s[k^1], tr[tr[x].s[k^1]].p = y;
tr[x].s[k^1] = y, tr[y].p = x;
pushup(y), pushup(x);
}
void splay(int x, int k)
{
while (tr[x].p != k)
{
int y = tr[x].p, z = tr[y].p;
if (z != k)
{
if ((tr[z].s[1] == y) ^ (tr[y].s[1] == x)) rotate(x);
else rotate(y);
}
rotate(x);
}
if (!k) root = x;
}
pushup
void pushup(int x)
{
Node &u = tr[x], &l = tr[u.s[0]], &r = tr[u.s[1]];
u.size = l.size + r.size + 1; // 一定要加上自己
u.sum = l.sum + r.sum + u.v;
u.ls = max(l.ls, l.sum + u.v + r.ls);
u.rs = max(r.rs, r.sum + u.v + l.rs);
u.ms = max(max(l.ms, r.ms), l.rs + u.v + r.ls);
}
pushdown
void pushdown(int x)
{
Node &u = tr[x], &l = tr[u.s[0]], &r = tr[u.s[1]];
if (u.same)
{
u.same = u.rev = 0; // 权值一样的话, 就不用翻转了
if (u.s[0]) l.same = 1, l.v = u.v, l.sum = l.v * l.size;
if (u.s[1]) r.same = 1, r.v = u.v, r.sum = r.v * r.size;
if (u.v > 0)
{
if (u.s[0]) l.ms = l.ls = l.rs = l.sum;
if (u.s[1]) r.ms = r.ls = r.rs = r.sum;
}
else
{
if (u.s[0]) l.ms = l.v, l.ls = l.rs = 0;
if (u.s[1]) r.ms = r.v, r.ls = r.rs = 0;
}
}
else if (u.rev) // 左中右 -> 右中左
{
u.rev = 0, l.rev ^= 1, r.rev ^= 1;
swap(l.ls, l.rs), swap(r.ls, r.rs);
swap(l.s[0], l.s[1]), swap(r.s[0], r.s[1]);
}
}
值为 $v$ 的元素的排名
int get(int v) // >= 的最小数
{
int u = root, res;
while (u)
{
if (v <= tr[u].v) res = u, u = tr[u].s[0];
else u = tr[u].s[1];
}
splay(res, 0);
return tr[tr[root].s[0]].size; // 返回的是 >= 的最小数的排名, 这里算了哨兵
}
void find(int v)
{
int u = root;
while (tr[u].v != v && tr[u].s[v > tr[u].v])
u = tr[u].s[v > tr[u].v];
splay(u, 0);
}
tr[tr[root].s[0]].size
求前驱后继
int next(int x, int f)
{
int u = root;
if ((tr[u].v > x && f) || (tr[u].v < x && !f)) // 严格
return tr[u].v;
u = tr[u].s[f];
while (tr[u].s[f^1])
u = tr[u].s[f^1];
return tr[u].v;
}
int next(int x, int f)
{
int u = root;
if ((tr[u].v >= x && f) || (tr[u].v <= x && !f)) // 非严格
return tr[u].v;
u = tr[u].s[f];
while (tr[u].s[f^1])
u = tr[u].s[f^1];
return tr[u].v;
}
求排名为 $k$ 的元素的下标
int get_k(int k) // 返回排名为 k 的元素的编号
{
int u = root;
while (u)
{
pushdown(u);
int l = tr[u].s[0], r = tr[u].s[1];
if (tr[l].size >= k) u = l;
else if (tr[l].size + 1 == k) return u;
else k -= tr[l].size + 1, u = r;
}
return -1;
}
tr[get_k(k + 1)].v // 排名为 k 的元素的值, + 1 是因为哨兵
区间删除
int posi, tot;
scanf("%d%d", &posi, &tot);
int l = get_k(posi), r = get_k(posi + tot + 1);
splay(l, 0), splay(r, l);
tr[r].s[0] = 0;
pushup(r), pushup(l);
区间建树
int build(int l, int r, int p)
{
int mid = l + r >> 1;
int u = ++ idx;
tr[u].init(w[mid], p);
if (l < mid) tr[u].s[0] = build(l, mid - 1, u);
if (mid < r) tr[u].s[1] = build(mid + 1, r, u);
pushup(u);
return u;
}
区间插入
int posi, tot;
scanf("%d%d", &posi, &tot);
for (int i = 0; i < tot; i ++ ) scanf("%d", &w[i]);
int l = get_k(posi + 1), r = get_k(posi + 2);
splay(l, 0), splay(r, l);
int u = build(0, tot - 1, r);
tr[r].s[0] = u;
pushup(r), pushup(l);
可重集
struct Node
{
int s[2], p, v;
int size, cnt;
void init(int _v, int _p)
{
p = _p, v = _v;
size = cnt = 1;
}
}tr[N];
伸展,元素上传,元素下传,知排名求编号,区间建树和部分非函数操作都会受到影响。
内存回收
for (int i = 1; i < N; i ++ ) nodes[ ++ tt] = i; // 内存创建
void dfs(int u)
{
if (tr[u].s[0]) dfs(tr[u].s[0]);
if (tr[u].s[1]) dfs(tr[u].s[1]);
nodes[ ++ tt] = u; // 删点时回收内存
}
int build(int l, int r, int p)
{
int mid = l + r >> 1;
int u = nodes[tt -- ]; // 加点时释放内存
tr[u].init(w[mid], p);
if (l < mid) tr[u].s[0] = build(l, mid - 1, u);
if (mid < r) tr[u].s[1] = build(mid + 1, r, u);
pushup(u);
return u;
}
多个平衡树
struct Node
{
int s[2], p, v, id; // id 是这个元素的初始编号
int size;
void init(int _v, int _id, int _p)
{
v = _v, id = _id, p = _p;
size = 1;
}
}tr[N];
int root[N], idx; // 处理各个连通块的平衡树
多个平衡树时的操作
void splay(int x, int k, int b) // 将 x 转到第 b 个平衡树的第 k 个位置下面
{
while (tr[x].p != k)
{
int y = tr[x].p, z = tr[y].p;
if (z != k)
{
if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
else rotate(y);
}
rotate(x);
}
if (!k) root[b] = x;
}
void insert(int v, int id, int b) // 将值为 v 的点插入到第 b 个平衡树里面
{
int u = root[b], p = 0;
while (u) p = u, u = tr[u].s[v > tr[u].v];
u = ++ idx;
if (p) tr[p].s[v > tr[p].v] = u;
tr[u].init(v, id, p);
splay(u, 0, b);
}
int get_k(int k, int b)
{
int u = root[b];
while (u)
{
int l = tr[u].s[0], r = tr[u].s[1];
if (tr[l].size >= k) u = l;
else if (tr[l].size + 1 == k) return tr[u].id;
else k -= tr[l].size + 1, u = r;
}
return -1;
}
平衡树合并
void dfs(int u, int b) // 将 a 的所有点插入到 b 里面
{
if (tr[u].s[0]) dfs(tr[u].s[0], b);
if (tr[u].s[1]) dfs(tr[u].s[1], b);
insert(tr[u].v, tr[u].id, b);
}
dfs(root[a], b);