子串的最大差(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);
}

浙公网安备 33010602011771号