bzoj5089最大连续子段和 分块+单调栈
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj5089最大连续子段和 分块+单调栈相关的知识,希望对你有一定的参考价值。
题目描述
输入
输出
样例输入
5 5
2 -3 0 4 -7
Q 1 2
Q 1 5
A 2 3 2
Q 2 5
Q 1 3
样例输出
2
4
6
3
题解
暴力 分块+单调栈维护凸包
考虑这个问题的一个简化版本:对整个序列区间加,对整个序列查询最大连续子段和。
我们对于每一个子区间,考虑区间和 $y$ 与区间加的总值 $x$ 的关系,显然是一个一次函数关系,斜率为区间长度,截距为原来的区间和。容易发现对于每个 $x$ 取最上方的直线(即选择最大连续子段和)的话,最终形成的是一个第一象限的下凸包的形式(可以参考 [JLOI2013]赛车)。
因此我们首先把这个下凸包求出来:每个斜率保留最上方的一条(即同一区间长度取区间和最大的),然后按照斜率从小到大加入当前直线、使用单调栈弹出不合法直线。这样我们就求出了这个上凸包。对于整体加和操作时,判断当前到达哪一条直线即可。由于加的只有正整数,因此这个移动过程最多只能进行 $n$ 次。
那么如果操作不是对整个序列进行的呢?可以考虑把序列分块,加和时整块如上处理,零碎部分暴力重构;查询时直接区间合并。由于要区间合并,因此还要维护从左开始的最大连续段和、从右开始的最大连续段和。处理方法与最大连续子段和相同。
分析一下这样做的时间复杂度:
设块的大小为 $si$
对于预处理操作,重构每个块的时间复杂度为 $O(si^2)$ ,重构 $\frac n{si}$ 个块的时间复杂度为 $O(n·si)$ ;
对于修改操作,一个块只有重构以后才能产生贡献 $O(si)$,重构的时间复杂度为 $O(si^2)$,每次修改操作只会重构最多两个块,因此单次修改操作的时间复杂度为 $O(si^2)$ ;
对于查询操作,整块拿出来区间信息是 $O(1)$ 的,因此查询操作的时间复杂度为 $O(\frac n{si}+si)$ 。
因此总的时间复杂度为 $O(m(\frac n{si}+si^2))$
根据均值不等式,当 $\frac n{si}=si^2$ 时这个复杂度最小,此时 $si=\sqrt[3]n$(实际上 $si=2\sqrt[3]n$ 时最优)
时间复杂度 $O(n^{\frac 53})$
然而暴力可以过我也是醉了。。。没办法卡不住啊╮(╯▽╰)╭
话说本题我原来还打算出一个带区间减的,只需要在凸包上二分即可,然而感觉更跑不过暴力于是就没出。。。
#include <cmath> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; inline ll cdiv(ll x , ll y) { return (x + y - 1) / y; } inline void solve(int c , ll *v , int *p , int &s , int &now) { int i; for(now = 1 , s = i = 0 ; i <= c ; i ++ ) { while(s && v[i] >= v[p[s]]) s -- ; while(s > 1 && (i - p[s]) * cdiv(v[p[s - 1]] - v[p[s]] , p[s] - p[s - 1]) >= v[p[s]] - v[i]) s -- ; p[++s] = i; } } inline void add(ll a , ll *v , int *p , int s , int &now) { while(now < s && p[now + 1] * a + v[p[now + 1]] >= p[now] * a + v[p[now]]) now ++ ; } struct data { int c , lp[80] , rp[80] , tp[80] , sl , sr , st , nowl , nowr , nowt; ll v[80] , sum , a , lv[80] , rv[80] , tv[80]; inline void build() { int i , j; ll s; sum = 0; for(i = 1 ; i <= c ; i ++ ) sum += v[i]; for(i = 1 ; i <= c ; i ++ ) lv[i] = lv[i - 1] + v[i]; for(i = 1 ; i <= c ; i ++ ) rv[i] = rv[i - 1] + v[c - i + 1]; for(i = 1 ; i <= c ; i ++ ) tv[i] = -1ll << 62; for(i = 1 ; i <= c ; i ++ ) { s = 0; for(j = i ; j <= c ; j ++ ) s += v[j] , tv[j - i + 1] = max(tv[j - i + 1] , s); } solve(c , lv , lp , sl , nowl); solve(c , rv , rp , sr , nowr); solve(c , tv , tp , st , nowt); } inline void update(ll v) { a += v; add(a , lv , lp , sl , nowl); add(a , rv , rp , sr , nowr); add(a , tv , tp , st , nowt); } }b[800]; ll a[50010]; char str[5]; int main() { int n , m , si , i , l , r , t; ll x , al , ar , at , as , tl , tr , tt , ts; scanf("%d%d" , &n , &m) , si = (int)cbrt(n) << 1; for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &a[i]); for(i = 0 ; i <= (n - 1) / si ; i ++ ) { for(b[i].c = 1 ; b[i].c <= si && b[i].c + i * si <= n ; b[i].c ++ ) b[i].v[b[i].c] = a[i * si + b[i].c]; b[i].c -- , b[i].build(); } while(m -- ) { scanf("%s%d%d" , str , &l , &r); if(str[0] == ‘A‘) { scanf("%lld" , &x); if((l - 1) / si == (r - 1) / si) { t = (l - 1) / si; for(i = l - t * si ; i <= r - t * si ; i ++ ) b[t].v[i] += x; for(i = 1 ; i <= b[t].c ; i ++ ) b[t].v[i] += b[t].a; b[t].a = 0 , b[t].build(); } else { t = (l - 1) / si; for(i = l - t * si ; i <= b[t].c ; i ++ ) b[t].v[i] += x; for(i = 1 ; i <= b[t].c ; i ++ ) b[t].v[i] += b[t].a; b[t].a = 0 , b[t].build(); for(i = (l - 1) / si + 1 ; i < (r - 1) / si ; i ++ ) b[i].update(x); t = (r - 1) / si; for(i = 1 ; i <= r - t * si ; i ++ ) b[t].v[i] += x; for(i = 1 ; i <= b[t].c ; i ++ ) b[t].v[i] += b[t].a; b[t].a = 0 , b[t].build(); } } else { as = al = ar = at = 0; if((l - 1) / si == (r - 1) / si) { t = (l - 1) / si; for(i = l - t * si ; i <= r - t * si ; i ++ ) { x = b[t].v[i] + b[t].a; at = max(at , ar + x); al = max(al , as + x); ar = max(ar + x , 0ll); as += x; } } else { t = (l - 1) / si; for(i = l - t * si ; i <= b[t].c ; i ++ ) { x = b[t].v[i] + b[t].a; at = max(at , ar + x); al = max(al , as + x); ar = max(ar + x , 0ll); as += x; } for(i = (l - 1) / si + 1 ; i < (r - 1) / si ; i ++ ) { tl = b[i].lp[b[i].nowl] * b[i].a + b[i].lv[b[i].lp[b[i].nowl]]; tr = b[i].rp[b[i].nowr] * b[i].a + b[i].rv[b[i].rp[b[i].nowr]]; tt = b[i].tp[b[i].nowt] * b[i].a + b[i].tv[b[i].tp[b[i].nowt]]; ts = b[i].sum + b[i].c * b[i].a; at = max(at , max(tt , ar + tl)); al = max(al , as + tl); ar = max(tr , ts + ar); as += ts; } t = (r - 1) / si; for(i = 1 ; i <= r - t * si ; i ++ ) { x = b[t].v[i] + b[t].a; at = max(at , ar + x); al = max(al , as + x); ar = max(ar + x , 0ll); as += x; } } printf("%lld\n" , at); } } return 0; }
以上是关于bzoj5089最大连续子段和 分块+单调栈的主要内容,如果未能解决你的问题,请参考以下文章