当前位置: 首页 > news >正文

FHQ Treap

按值分裂

/* 按值x分裂Treap:将树u分裂为<=x的树l和>x的树r */
void split(int u, int x, int& l, int& r) {if (!u) { l = r = 0; return; }  // 空树直接返回if (t[u].val <= x) {            // 当前节点值<=x,应放入左树l = u;                      // 当前节点作为左树的根split(t[u].rs, x, t[u].rs, r); // 递归处理右子树} else {                        // 当前节点值>x,应放入右树r = u;                      // 当前节点作为右树的根split(t[u].ls, x, l, t[u].ls); // 递归处理左子树}pushup(u);                      // 更新当前节点子树大小
}/* 合并两棵Treap:合并左树l和右树r */
int merge(int l, int r) {if (!l || !r) return l + r;     // 任意一棵树为空则返回另一棵// 按照优先级合并(维护堆性质)if (t[l].pri > t[r].pri) {      // 左树根优先级更高t[l].rs = merge(t[l].rs, r); // 将右树合并到左树的右子树pushup(l);                  // 更新左树大小return l;                   // 返回合并后的左树} else {                        // 右树根优先级更高t[r].ls = merge(l, t[r].ls); // 将左树合并到右树的左子树pushup(r);                  // 更新右树大小return r;                   // 返回合并后的右树}
}/* 插入值x */
void add(int x) {int l, r;split(root, x, l, r);           // 按x分裂原树newnode(x);                     // 创建新节点root = merge(merge(l, cnt), r); // 合并左树、新节点、右树
}/* 删除值x */
void del(int x) {int l, r, p;split(root, x, l, r);           // 按x分裂原树split(l, x - 1, l, p);          // 在左树中按x-1分裂,得到等于x的节点pp = merge(t[p].ls, t[p].rs);    // 合并p的左右子树(相当于删除p)root = merge(merge(l, p), r);   // 合并所有部分
}/* 查询x的排名(比x小的数的个数+1) */
int Rank(int x) {int l, r;split(root, x - 1, l, r);       // 按x-1分裂,左树包含所有<x的值int res = t[l].siz + 1;         // 排名=左树大小+1root = merge(l, r);             // 合并回原树return res;
}/* 查询第k小的数(递归实现) */
int kth(int u, int k) {if (k == t[t[u].ls].siz + 1) return t[u].val;  // 找到目标节点if (k > t[t[u].ls].siz + 1)                   // 在右子树中查找return kth(t[u].rs, k - (t[t[u].ls].siz + 1));return kth(t[u].ls, k);                       // 在左子树中查找
}/* 查询前驱(type=0)或后继(type=1) */
int nex(int x, int type) {int l, r;split(root, x - !type, l, r);   // type=0分裂为<=x-1和>x-1;type=1分裂为<=x和>xint res;if (!type) res = kth(l, t[l].siz); // 前驱是左树的最大值else res = kth(r, 1);            // 后继是右树的最小值root = merge(l, r);             // 合并回原树return res;
}

按排名分裂

rank与kth代码一样

void split(int u, int k, int& l, int& r) {if (!u) { l = r = 0; return; }if (t[t[u].ls].siz + 1 <= k) {  // 当前节点属于左树l = u;split(t[u].rs, k - t[t[u].ls].siz - 1, t[u].rs, r);}else {                        // 当前节点属于右树r = u;split(t[u].ls, k, l, t[u].ls);}pushup(u);
}/* 合并两棵树 */
int merge(int l, int r) {if (!l || !r) return l + r;if (t[l].pri > t[r].pri) {t[l].rs = merge(t[l].rs, r);pushup(l);return l;}else {t[r].ls = merge(l, t[r].ls);pushup(r);return r;}
}/* 插入值x */
void insert(int x) {int l, r;split(root, Rank(x) - 1, l, r); // 按排名分裂newnode(x);root = merge(merge(l, cnt), r);
}/* 删除值x */
void del(int x) {int l, r, p;split(root, Rank(x) - 1, l, r);split(r, 1, p, r);      // 分离出要删除的节点root = merge(l, r);             // 跳过被删除节点
}/* 查询前驱(type=0)或后继(type=1) */
int nex(int x, int type) {int l, r;split(root, Rank(x) - !type, l, r);int res = type ? kth(1) : kth(t[l].siz);root = merge(l, r);return res;
}

可持久化

P5055 【模板】可持久化文艺平衡树

题目描述

您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):

  1. 在第 p p p 个数后插入数 x x x
  2. 删除第 p p p 个数。
  3. 翻转区间 [ l , r ] [l,r] [l,r],例如原序列是 { 5 , 4 , 3 , 2 , 1 } \{5,4,3,2,1\} {5,4,3,2,1},翻转区间 [ 2 , 4 ] [2,4] [2,4] 后,结果是 { 5 , 2 , 3 , 4 , 1 } \{5,2,3,4,1\} {5,2,3,4,1}
  4. 查询区间 [ l , r ] [l,r] [l,r] 中所有数的和。

和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 4 4 4 即保持原版本无变化),新版本即编号为此次操作的序号。

本题强制在线。

输入格式

第一行包含一个整数 n n n,表示操作的总数。

接下来 n n n 行,每行前两个整数 v i , o p t i v_i, \mathrm{opt}_i vi,opti v i v_i vi 表示基于的过去版本号( 0 ≤ v i < i 0 \le v_i < i 0vi<i), o p t i \mathrm{opt}_i opti 表示操作的序号( 1 ≤ o p t i ≤ 4 1 \le \mathrm{opt}_i \le 4 1opti4)。

o p t i = 1 \mathrm{opt}_i=1 opti=1,则接下来两个整数 p i , x i p_i, x_i pi,xi,表示操作为在第 p i p_i pi 个数后插入数 x x x
o p t i = 2 \mathrm{opt}_i=2 opti=2,则接下来一个整数 p i p_i pi,表示操作为删除第 p i p_i pi 个数。
o p t i = 3 \mathrm{opt}_i=3 opti=3,则接下来两个整数 l i , r i l_i, r_i li,ri,表示操作为翻转区间 [ l i , r i ] [l_i, r_i] [li,ri]
o p t i = 4 \mathrm{opt}_i=4 opti=4,则接下来两个整数 l i , r i l_i, r_i li,ri,表示操作为查询区间 [ l i , r i ] [l_i, r_i] [li,ri] 的和。

强制在线规则:
令当前操作之前的最后一次 4 4 4 操作的答案为 l a s t a n s lastans lastans(如果之前没有 4 4 4 操作,则 l a s t a n s = 0 lastans=0 lastans=0)。
则此次操作的 p i , x i p_i,x_i pi,xi l i , r i l_i,r_i li,ri 均按位异或上 l a s t a n s lastans lastans 即可得到真实的 p i , x i p_i,x_i pi,xi l i , r i l_i,r_i li,ri

输出格式

对于每个序号为 4 4 4 的查询操作,输出一行一个数表示区间的和。

// 复制树节点,只需复制根节点相关信息
int clone(int u) {int ret = new_node(0);  // 新建一个节点t[ret] = t[u];  // 复制节点u的信息到新节点return ret;
}
// 更新节点信息,包括子树大小和权值和
void Update(int u) {t[u].size = t[t[u].ls].size + t[t[u].rs].size + 1;t[u].sum = t[t[u].ls].sum + t[t[u].rs].sum + t[u].val;
}
// 下传懒标记
void push_down(int u) {if (!t[u].lazy) return;  // 若无懒标记,直接返回if (t[u].ls != 0) t[u].ls = clone(t[u].ls);  // 复制左子树if (t[u].rs != 0) t[u].rs = clone(t[u].rs);  // 复制右子树swap(t[u].ls, t[u].rs);  // 交换左右子树t[t[u].ls].lazy ^= 1;  // 左子树懒标记取反t[t[u].rs].lazy ^= 1;  // 右子树懒标记取反t[u].lazy = 0;  // 清空当前节点懒标记
}
// 按排名分裂操作
void Split(int u, int x, int &L, int &R) {if (u == 0) {L = R = 0; return; }  // 若节点为空,左右子树设为0并返回push_down(u);  // 下传懒标记if (t[t[u].ls].size + 1 <= x) {  // 第x个数在u的右子树上L = clone(u);  // 复制当前节点作为左子树Split(t[L].rs, x - t[t[u].ls].size - 1, t[L].rs, R);  // 递归分裂右子树Update(L);  // 更新左子树信息}else {R = clone(u);  // 复制当前节点作为右子树Split(t[R].ls, x, L, t[R].ls);  // 递归分裂左子树Update(R);  // 更新右子树信息}
}
// 合并操作
int Merge(int L, int R) {if (L == 0 || R == 0) return L + R;  // 若有一个子树为空,返回另一个子树push_down(L);  // 下传左子树懒标记push_down(R);  // 下传右子树懒标记if (t[L].pri > t[R].pri) {  // 根据优先级合并t[L].rs = Merge(t[L].rs, R);  // 递归合并右子树和RUpdate(L);  // 更新左子树信息return L;}else {t[R].ls = Merge(L, t[R].ls);  // 递归合并L和左子树Update(R);  // 更新右子树信息return R;}
}if (op == 3) {  // 翻转区间[x, y]cin >> x >> y;x ^= lastans;y ^= lastans;Split(root[v], y, L, R);  // 分裂出前y个数Split(L, x - 1, L, P);  // 分裂出区间[x, y]t[P].lazy ^= 1;  // 对区间打懒标记root[++version] = Merge(Merge(L, P), R);  // 合并并记录新版本}if (op == 4) {  // 查询区间和[x, y]cin >> x >> y;x ^= lastans;y ^= lastans;Split(root[v], y, L, R);  // 分裂出前y个数Split(L, x - 1, L, P);  // 分裂出区间[x, y]lastans = t[P].sum;  // 记录区间和cout << lastans << endl;root[++version] = Merge(Merge(L, P), R);  // 合并并记录新版本}

相关文章:

  • AI作画提示词:Prompts工程技巧与最佳实践
  • MYSQL-设计表
  • 消息队列的作用
  • [Verilog]跨时钟域数据传输解决方案
  • 2025五一数学建模竞赛A题完整分析论文(共45页)(含模型、可运行代码、数据)
  • spring中的@PostConstruct注解详解
  • 支持向量机(SVM)详解
  • 报文三次握手对么٩(๑^o^๑)۶
  • 网络原理 - 13(HTTP/HTTPS - 4 - HTTPS)
  • 从数据到故事:用可视化工具讲好商业“话本“
  • 鼎讯信通 智能通信干扰设备:多频段多模态信号压制解决方案
  • PS插件生态深度解析与精品合集推荐
  • 姜老师MBTI人格分析课程2:ENFP
  • ppt设计美化公司_杰青_长江学者_优青_青年长江学者_万人计划青年拔尖人才答辩ppt模板
  • 深入解析C++11 auto关键字:类型推导的现代实践
  • 【JS压缩图片】js压缩图片至指定大小
  • Flutter - 概览
  • 深入解析C++11委托构造函数:消除冗余初始化的利器
  • 【CVE-2025-1094】:PostgreSQL 14.15 SQL注入漏洞导致的RCE_ 利用代码和分析
  • AUTOSAR图解==>AUTOSAR_SRS_BusMirroring
  • 重庆渝中警方:男子点燃摩托车欲寻衅滋事,被民警和群众合力制服
  • 香港发生车祸致22人受伤,4人伤势严重
  • 德国旅游胜地发生爆炸事故,11人受伤
  • AI世界的年轻人,如何作答未来
  • 上海科创再出发:“造星”的城和“摘星”的人
  • 南京106亿元成交19宗涉宅地块:建邺区地块楼面单价重回4.5万元