和主席树类似,开一个 rot[N]
数组记录每个版本的根结点即可
int root, idx; // 分别表示根结点编号和当前用到哪个结点
int val[N * 70]; // 结点权值
int pri[N * 70]; // 结点优先级
int sz[N * 70]; // 结点子树大小
int ch[N * 70][2]; // 结点左右儿子
int tag[N * 70]; // 标记
int rot[N]; // 根结点
int sum[N * 70]; // 子段和
void getnode(int x) // 创建权值为x的新结点
{
sz[++ idx] = 1;
ch[idx][0] = ch[idx][1] = 0;
val[idx] = x;
tag[idx] = 0;
sum[idx] = x;
pri[idx] = rand(); // 优先值取随机数保证树形状随机
}
void copynode(int x, int y) // 把结点y的信息复制给x
{
val[x] = val[y];
pri[x] = pri[y];
sz[x] = sz[y];
ch[x][0] = ch[y][0];
ch[x][1] = ch[y][1];
tag[x] = tag[y];
sum[x] = sum[y];
}
void pushup(int u) // 更新结点u信息
{
sz[u] = sz[ch[u][0]] + sz[ch[u][1]] + 1;
sum[u] = sum[ch[u][0]] + sum[ch[u][1]] + val[u];
}
void pushdown(int u) // 懒标记下传
{
if (tag[u])
{
int p;
if (ch[u][0])
{
p = ++ idx;
copynode(p, ch[u][0]);
ch[u][0] = p;
}
if (ch[u][1])
{
p = ++ idx;
copynode(p, ch[u][1]);
ch[u][1] = p;
}
swap(ch[u][0], ch[u][1]);
if (ch[u][0]) tag[ch[u][0]] ^= 1;
if (ch[u][1]) tag[ch[u][1]] ^= 1;
tag[u] = 0;
}
}
void split(int u, int x, int& lt, int& rt) // 把treap分成小于等于x和大于x的两个部分
{
if (u == 0)
{
lt = rt = 0;
return;
}
pushdown(u);
if (sz[ch[u][0]] + 1 <= x) // 当前点小于x,说明分界点在右儿子
{
lt = ++ idx;
copynode(lt, u);
split(ch[lt][1], x - (sz[ch[u][0]] + 1), ch[lt][1], rt);
pushup(lt);
}
else // 当前点大于x,说明分界点在左儿子
{
rt = ++ idx;
copynode(rt, u);
split(ch[rt][0], x, lt, ch[rt][0]);
pushup(rt);
}
}
int merge(int lt, int rt) // 合并根结点编号为lt和rt的两棵treap
{
if (lt == 0 || rt == 0) return lt + rt;
if (pri[lt] > pri[rt]) // lt的优先级比rt大 就把rt合并到lt的右子树
{
pushdown(lt);
ch[lt][1] = merge(ch[lt][1], rt);
pushup(lt);
return lt;
}
else // rt的优先级比lt大 就把lt合并到rt的左子树
{
pushdown(rt);
ch[rt][0] = merge(lt, ch[rt][0]);
pushup(rt);
return rt;
}
}
void insert(int& rot, int p, int x) // 将权值为x的新结点插入根结点为rot的treap
{
int lt, rt;
split(rot, p, lt, rt); // 先把原treap分成小于等于x和大于x两个部分
getnode(x); // 创建值为x的结点
rot = merge(merge(lt, idx), rt); // 先合并小于等于x的子树和新结点 再将其与大于x的子树合并
return;
}
void del(int& rot, int x)
{
int lt, md, rt;
split(rot, x, lt, rt); // 先把原treap分成小于等于x和大于x两个部分
split(lt, x - 1, lt, md); // 再把小于等于x的部分分成小于等于x-1和大于x-1两个部分
// 此时以md为根结点的子树内所有结点的权值都是x
md = merge(ch[md][0], ch[md][1]); // 合并md的左右儿子 即删去md这个结点
rot = merge(lt, rt); // 先合并小于等于x-1的部分和等于x的部分 再将其与大于x的子树合并
return;
}