子串的最大差(camp02-26每日一题)

#436. 子串的最大差

这道题与力扣的这一题cf上的这一题一样。//如果原网址被ban了就用这两个。

问题

给定有n个数的数组a,求每个子串的最大值减最小值。

问题转换

假设数列\(a[n]\)一共包含m个子串segment,那么:

$最大差sum = \sum_{i=0}^m (segment[i]的最大值 - segment[i]的最小值) $

即:$ sum = \sum_{i=0}^m segment[i]的最大值 - \sum_{i=0}^m segment[i]的最小值$

因此,我们需要分别求出每个\(segment\)的最大值和最小值,并两者相减。

在此使用单调栈算法。(如果不会,请看动画演示学习)

单调栈的使用

对于每个segment,如果一个数字a[i]在此区间里没有比它大的,那么此数字就是最大值,于是我们可以判断,我们寻找这个数字的左右两边第一个比它大的值,那么在这两个值之间包含a[i]的区间,a[i]一定是最大值。

最小值同理。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6;
class Node {
	public:
		long long int val,num;
		int lma,rma;
		int lmi,rmi;
};
Node a[N];
long long subArrayRanges(int n) {
	stack<Node>icrs,dcrs; //升降
	icrs.push(a[1]);
	dcrs.push(a[1]);
	for(int i = 2 ; i <= n ; i++) {
		while(icrs.size() && icrs.top().val <= a[i].val ) { //升
			int j = icrs.top().num;
			a[j].rma = i;
			icrs.pop();
		}
		if(icrs.size())
			a[i].lma = icrs.top().num;
		else a[i].lma = 0;
		icrs.push(a[i]);

		while(dcrs.size() && dcrs.top().val >= a[i].val ) { //降
			int j = dcrs.top().num;
			a[j].rmi = i;
			dcrs.pop();
		}
		if(dcrs.size())
			a[i].lmi = dcrs.top().num;
		else a[i].lmi = 0;
		dcrs.push(a[i]);
	}
	while(icrs.size()) {
		int j = icrs.top().num;
		icrs.pop();
		a[j].rma = n+1;
		icrs.size();
	}
	while(dcrs.size()) {
		int j = dcrs.top().num;
		dcrs.pop();
		a[j].rmi = n+1;
		dcrs.size();
	}
	long long sum = 0;
	for(int i = 1 ; i <= n ; i++) {
		sum += a[i].val*(a[i].rma - i)*(i - a[i].lma);
		sum -= a[i].val*(a[i].rmi - i)*(i - a[i].lmi);
	}
	return sum;
}
int main() {
	int n;
	cin >> n;
	for(int i = 1 ; i <= n ; i++) {
		cin >> a[i].val;
		a[i].num = i;
	}
	cout << subArrayRanges(n);
}
posted @ 2022-02-28 17:13  seekerHeron  阅读(171)  评论(0)    收藏  举报