• 【bzoj5089】最大连续子段和 分块+单调栈维护凸包


    题目描述

    给出一个长度为 n 的序列,要求支持如下两种操作:
    A  l  r  x :将 [l,r] 区间内的所有数加上 x ;
    Q  l  r : 询问 [l,r] 区间的最大连续子段和。
    其中,一个区间的最大连续子段和指的是:该区间所有子区间的区间和中的最大值(本题中子区间包括空区间,区间和为 0 )。

    输入

    第一行两个整数 n、m,表示序列的长度以及操作的数目。
    之后的 m 行,每行输入一个操作,含义如题目所述。保证操作为  A  l  r  x  或  Q  l  r  之一。
    对于 30% 的数据,n,m≤300 ;
    对于 60% 的数据,n,m≤1000 ;
    对于 100% 的数据,1≤n,m≤50000, |a_i|≤10^9, 1≤x≤40000, 1≤l,r≤n

    输出

    每个 Q 操作输出一行,表示询问区间的最大连续子段和。

    样例输入

    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=2sqrt[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
    " , at);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Kafka使用代码设置offset值
    单机wordcount
    队列:队列在线程池等有限资源池中的应用
    栈:如何实现浏览器的前进和后退功能?
    Codeforces Beta Round #57 (Div. 2) E. Enemy is weak
    Codeforces Round #345 (Div. 1) A. Watchmen
    Codeforces Round #291 (Div. 2) B. Han Solo and Lazer Gun
    zoj4027 Sequence Swapping
    zoj4028 LIS
    zoj 3946 Highway Project
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7886654.html
Copyright © 2020-2023  润新知