专栏文章
线段树上二分学习笔记
算法·理论参与者 7已保存评论 7
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 7 条
- 当前快照
- 2 份
- 快照标识符
- @miijbsi4
- 此快照首次捕获于
- 2025/11/28 15:22 3 个月前
- 此快照最后确认于
- 2025/12/02 01:10 3 个月前
update: 题面描述不准确,原题是一个 的函数,也就是说是一条曲线。
在 NOIP 模拟赛里,我遇到了这么一道题:
给你一个相邻两位相差为一的序列,支持两种操作:
- 给区间 加上一个值。
- 查询区间 中第一个等于 的下标,保证原序列满足 。

当时在考场上,我虽然想到了二分,但没想到最小值具有单调性,结果坠机了。考完以后,我琢磨出了一个做法:先暴力建一棵线段树,然后在这棵树上二分找第一个大于等于 的数。不过这样时间复杂度是 ,明显不优。如果换成其他 RMQ 结构,虽然查询快,但区间修改又不方便了。这时候就需要我们的主角——线段树上二分。
换个更形象的说法,其实就是在线段树上游走。基本思路就是直接利用左右子树的信息,判断答案可能在左边还是右边,然后直接往可能有解的那边走。对于查询区间 ,我们只需要像普通线段树查询一样,只处理和目标区间有交集的节点。如果一个节点区间没有被完全包含在目标区间里,那它就不能直接给出答案,得继续往下找。
例题
这道题跟我上面说的差不多,就是多了点小细节。
参考代码
CPPint query(int p,int l,int r,int x)
{
if(tr[p].mn>x)
return INF;// 当前区间最小值都比x大,肯定没戏
if(tr[p].l==tr[p].r)// 走到叶子了
return tr[p].mn<=x?tr[p].l:INF; // 看看这个点行不行
if(tr[id].l>=l&&tr[p].r<=r)
{ // 这个节点完全在查询区间里
if(tr[lt].mn<=x)
return query(rt,l,r,x); // 左边可能有答案
else
return query(rt,l,r,x); // 左边不行就去右边
}
int ans=INF,mid=(tr[p].l+tr[p].r)>>1;
if(l<=mid)
ans=query(lt,l,r,x); // 左边跟查询区间有重叠,优先找i左边
if(ans==INF&&r>mid)
ans=query(rt,l,r,x); // 左边没找到且右边有重叠
return ans;
}
总结
线段树上二分其实就是在线段树的子树里中进行即使决策,优先往左边走,只处理跟查询区间有交集的节点,这样就能高效地找到答案。
相关推荐
评论
共 7 条评论,欢迎与作者交流。
正在加载评论...