【题解】P5073 | 凸包 线段树 空间优化
常见的对于某个
本题的其它题解都维护了第一种这种不太直观的斜率优化方式,还要使用闵可夫斯基和,在这里我是用的第二种方式,较为直观。
全局加,区间最大子段和。
考虑类似普通分治形带修最大子段和的方式维护,使用线段树的方式维护如下四个值:区间和,区间前缀和最值,区间后缀和最值,区间最大子段和最值。
现在我们拓展这个做法,来考虑这些值随着全局增加量
首先有区间和为一次函数
区间前缀和和区间后缀和没有本质区别,在这里拿区间前缀和分析:
令
接下来是最大子段和的变化,其也是一些一次函数的最值,所以也是凸函数
但是与上面不同的是,我们难以低于
因为我们在上面证明了这个
直接建出这样一颗线段数,每个点上维护这样三个凸包,查询时在对应凸包上二分求出三个最值,然后就像普通的最大子段和那样合并就行了。
时间复杂度
因为我们的询问的信息只和目前增加的
空间优化同样地考虑离线询问后使用 “线段树分治线性空间” 的 Trick,直接带着所有询问向下递归,判断该询问在该区间是否生效,是否下放到左右儿子即可(下放后要释放空间或是直接使用单链表移过去下放!),这样每个时刻每个询问至多只会在两个节点上。
我们得到了一个时间
一种很优秀的写法是不建出成型的线段树,在递归时直接下传询问,返回凸包,这样每个时刻一个询问只会存在在至多两个节点上(线段树上其跨过
这是关键部分的代码:
std :: tuple < covex , covex , covex , line > solve (int l,int r,std :: vector <int> &ques) {
int mid = (l + r) >> 1;
std :: vector <int> ls , rs , ths ;
for (auto q : ques) {
if (ql[q] <= l && r <= qr[q]) {
ths.emplace_back(q) ;
continue;
}
if (ql[q] <= mid) ls.emplace_back(q) ;
if (qr[q] > mid) rs.emplace_back(q) ;
}
ls.shrink_to_fit() , rs.shrink_to_fit( ) ;
std :: vector<int> ().swap (ques) ;
covex pre , suf , sub ; line sum ;
if (l == r) {
pre.f.emplace_back(0 , 0) ;
pre.f.emplace_back(1 , a[l]) ;
suf = pre ;
sub = pre ;
sum = line (1 , a[l]) ;
}
else {
auto [prel , sufl , subl , suml] = solve (l , mid , ls) ;
auto [prer , sufr , subr , sumr] = solve (mid + 1 , r , rs) ;
sum = suml + sumr ;
/*
pre = max (prel , prer + suml)
suf = max (sufr , prel + sufr)
sub = max (subl , subr , prer + sufl)
*/
sub.sum (sufl , prer) ;
sub.max (subl) ;
sub.max (subr) ;
prer.shift (suml) ;
sufl.shift (sumr) ;
pre.swap (prel) ;
suf.swap (sufr) ;
pre.max (prer) ;
suf.max (sufl) ;
}
std :: vector<line> &c1 = pre.f , &c2 = suf.f , &c3 = sub.f ;
int pt1 = 0 , pt2 = 0 , pt3 = 0 , s1 = (int)pre.f.size() - 1 , s2 = (int)suf.f.size( ) - 1 , s3 = (int)sub.f.size( ) - 1 ;
for (const auto &q:ths) {
ll del = qx[q] ;
ll qpre , qsuf , qsub , qsum = sum(del) ;
while (pt1 < s1 && c1[pt1](del) <= c1[pt1 + 1](del))
++ pt1 ;
qpre = c1[pt1] (del) ;
while (pt2 < s2 && c2[pt2](del) <= c2[pt2 + 1](del))
++ pt2 ;
qsuf = c2[pt2] (del) ;
while (pt3 < s3 && c3[pt3](del) <= c3[pt3 + 1](del))
++ pt3 ;
qsub = c3[pt3] (del) ;
ans[q] = std :: max (ans[q] , sf[q] + qpre ) ;
ans[q] = std :: max (ans[q] , qsub ) ;
sf[q] = std :: max (qsuf , qsum + sf[q]) ;
}
return {pre , suf , sub , sum} ;
}
这是完整的代码。