算法 {查询区间连续子数组1的最大长度}
普通版本
初始时A[111...111]
全是1, 每一次 将A[x] = 0
, 然后查询[l-r]
里面 连续1的最长长度, 比如[110111]
则l=1, r=4 (1011)
最长长度是11 (2)
;
这可以用线段树维护:
[l...r]
, Ma: 就表示答案(即这个区间的连续1的最大长度)
, MaPre: 从[l]开始的连续1的长度(A[l]如果为0则MaPre=0)
, MaSuf: 从[r]开始的连续1
;
void Merge( Info_ const& _lef, Info_ const& _rig){ // `lef=[0-1]; rig=[2-3]` 则当前区间是`[0-3]`, 你需要根据`lef,rig`的信息 来推导出`[0-3]`区间的信息;
Interval = {_lef.Interval.first, _rig.Interval.second};
Ma = std::max( {_lef.Ma, _rig.Ma, _lef.MaSuf + _rig.MaPre});
MaPre = (_lef.MaPre == _lef.Length() ? _lef.Length()+_rig.MaPre : _lef.MaPre);
MaSuf = (_rig.MaSuf == _rig.Length() ? _rig.Length()+_lef.MaSuf : _rig.MaSuf);
}
隔板版本
初始时A[111...111]
全是1, 每一次 在A[x,x+1]
的中间 放一个隔板, 然后查询[l-r]
里面 连续1的最长长度, 比如[111(0)111]
则1(0)11
的最长长度是11 (2)
;
还是用线段树 节点信息和含义 和普通版本的完全一样, 唯一不同的 是Modify_index
;
比如 我们要在x,x+1
的中间 放一个隔板, 那么此时要修改: 将节点[x,x]
的MaSuf = 0
这表示 他不可以和x+1
拼接, 同时将[x+1,x+1
节点的MaPre=0
;
Modify_Index(){
//>> 使用`_modVal`来更新`info`;
if( _modVal == 0){
info.MaSuf = 0;
}
else{
info.MaPre = 0;
}
}
修改操作 即在`(x,x+1)`之间放一个隔板
Seg.Modify_index( x, 0);
Seg.Modify_index( x+1, 1);
应用
@LINK: https://leetcode.cn/problems/block-placement-queries/description/
;
模板题;
错误
先看一个思路 (我没试 不保证正确), 把隔板 也看作是元素 转换成上面的普通版本, 即原数组[0,1,2]
对应新数组[0,(1),2,(3),4]
奇数下标为隔板, 每次查询时 做下标转换后 查询最长长度 去掉隔板的;
哦, 这是错误的…, 因为 比如最长长度是[l...r]
但是你不知道他里面有多少个隔板啊 因为你不知道l,r
你只知道r-l+1
的值;