线段树上二分
线段树上二分
费劲学的,得单领出来说说。网上有没有多少详细文章,有例题。
线段树的奇幻科技——线段树上二分 - Mercury_City - 博客园 (cnblogs.com) 这篇博客讲的好,但仍不详细。
不是二分+线段树,是直接利用线段树去二分查找。从而把
基本原理是直接利用当前的左子树和右子树,去判断目的解是在左还是右,然后直接进入有解的子树。而对于限制区间,只需要按正常查询的思维,只去与目标区间相交(包括在之内的情况)的区间。因为如果这个区间不完全包含在目标区间内,那么提供的信息就不准确,所以要到完全包含的区间内提供信息。看了代码应该就理解这句话了。
我研究了得一天,下面给出我测试所用的代码包,和这个优化的效果。
一个基本的例题是查找区间
int query(int u, int x) { if (tr[u].l == tr[u].r) return tr[u].l; else { int mid = l + r >> 1; if (tr[u << 1].maxv > x) return query(u << 1, l, r, x); // 如果左边有就直接去左边 else return query(u << 1 | 1, l, r, x); // 否则一定在右边 } }
但是这会导致一个问题,即如果不存在这样的数,我们没法表示出,所以要表示无解情况,一般以 -1 作为无解情况。即:
int query(int u, int x) { if (tr[u].l == tr[u].r) { if (tr[u].maxv <= x) return -1; return tr[u].l; } else { int mid = l + r >> 1; // 多余了 if (tr[u << 1].maxv > x) return query(u << 1, l, r, x); else return query(u << 1 | 1, l, r, x); } }
每次要么去左边要么去右边,所以时间复杂度一定
如果限制区间为
对于包含则进行上面那样的二分。如果到了对应节点就返回信息。实际上对于不相交的情况,我们不用管他,只进行相交的进入子树即可。
于是可以写出以下代码
int query(int u, int l, int r, int x) { if (tr[u].l == tr[u].r) // 找到一个解 { if (tr[u].maxv <= x) return -1; // 去除无效解 return tr[u].l; } else if (l <= tr[u].l && tr[u].r <= r) // 可以提供信息 { if (tr[u << 1].maxv > x) return query(u << 1, l, r, x); else return query(u << 1 | 1, l, r, x); } else // 仅相交,要继续划分区间,直到被包含 { int mid = tr[u].l + tr[u].r >> 1; int res = -1; if (l <= mid) res = query(u << 1, l, r, x); // 去含有的区间内, 和正常query一样 if (res == -1 && r > mid) res = query(u << 1 | 1, l, r, x); return res; } }
整体思路就是,正常 query 进限制区间,在区间内二分,直到有解返回。
二分是可以封边界的,上面代码就封左边界,即找尽量靠左的符合条件的点。对于封左/右边界,你只需要让它趋于进入左/右子树即可。给出对应封右的代码。
int query(int u, int l, int r, int x) { if (tr[u].l == tr[u].r) { if (tr[u].maxv <= x) return -1; return tr[u].l; } else if (l <= tr[u].l && tr[u].r <= r) { if (tr[u << 1 | 1].maxv > x) return query(u << 1 | 1, l, r, x); // 尽量先进右子树 else return query(u << 1, l, r, x); } else { int mid = tr[u].l + tr[u].r >> 1; int res = -1; if (r > mid) res = query(u << 1 | 1, l, r, x); // 同理 if (res == -1 && l <= mid) res = query(u << 1, l, r, x); return res; } }
测试文件: https://pan.axianyu.cn/f/3vqFn/线段树二分.zip
例题
来做做例题把,我费劲自己搞得 U502676 线段树上二分模版 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
还有一个例题 P11217 【MX-S4-T1】「yyOI R2」youyou 的垃圾桶 - blind5883 - 博客园 (cnblogs.com)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下