WOJ 1618 - Magic Array (线段树+单调栈)

本文介绍了如何解决求解所有子区间的最大值、最小值及长度乘积之和的问题,通过线段树和单调栈进行优化,从而避免超时。文章详细解析了线段树的维护过程,包括最大值、最小值的更新以及如何找到最大值的更新左端点。经过三次优化,最终将算法时间降低到1.5秒。
摘要由CSDN通过智能技术生成

题意:给定n(n<=500000)个数,A[1],A[2],...,A[n]。求所有子区间的 (最大值*最小值*长度)之和,对 10^9 取余数。


思路一:暴力     时间复杂度O(n^3)    超时

枚举所有子区间,然后遍历一遍来求最大值和最小值,然后累加答案。


思路二:稍加优化    时间复杂度O(n^2)   超时

枚举R,然后对于每个L<=R,用Max[L]表示区间[L,R]的最大值,Min[L]表示区间[L..R]的最小值。

在R增加1之后,每个L<=旧R的Max[L]和Min[L]只需要用A[R]去更新就行了。

代码:

//Simple solve
int Min[maxn],Max[maxn];
int SimpleSolve(){
	LL ANS=0;
	for(int R=1;R <=n;++R){
		Min[R]=Max[R]=A[R];
		for(int L=1;L <= R;++L){
			//更新Min[L]和Max[L] 
			Min[L]=min(Min[L],A[R]);
			Max[L]=max(Max[L],A[R]);
			//累加区间[L,R]的答案 
			ANS=(ANS+(LL)Min[L]*Max[L]%MOD*(R-L+1))%MOD;
		}
	}
	return ANS;
}


思路三: 线段树优化   时间复杂度:O(n*log(n))


思路三是思路二的线段树优化,所以在看思路三之前,请确保看懂了思路二。

思路二中,对于每个R,用A[R]更新了[1..R]的最大值和最小值,然后累加了[1..R]到R的答案。

如果更新和求和都使用线段树的话,时间复杂度就变成了O(n*log(n))。


首先,如何用线段树维护最大值。

对于每个R,要将Max[1..R]的数组中,所有小于A[R]的都更新成A[R]。

注意到Max[1],Max[2],...,Max[R]是单调非增的数列。

(Max[1]代表区间[1..R]的最大值,Max[2]代表区间[2..R]的最大值,明显Max[1]>=Max[2].)

所以,实际上,从某下标L开始,Max[L..R]都小于A[R],于是变成了线段树的区间修改:将[L..R]的数变成A[R]。

那么,怎么找到L值呢?在线段树的节点上多维护一个最大值的最大值,然后就可以判断了,具体看代码。


最小值同理。


然后来谈一谈线段树每个节点需要的变量,以下用m表示最小值,用M表示最大值,用L表示长度。

变量分为:标记量和统计量。


先来谈标记量,需要最小值标记,最大值标记,和长度标记,记做m,M,L。

毕竟是区间修改,所以需要三种标记。


统计量

然后明显要有(最大值*最小值*长度)的和,记做 smML。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值