这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!
一、题目大意
codeforce的题目还是一如既往的简单明了。题目的大致意思就是给你一个数组,要你求数组所有连续子序列的最大值-最小值之和。
二、题目思路以及AC代码
这看到题目之后,第一反应想出了O(n^2)的解决思路(直接暴力嘛),虽然知道肯定超时,还是尝试性的提交了一下,嗯,不错,过了11个数据。
好,下面就说一般思路。首先题目要求的是所有连续子序列的最大值-最小值之和,我们可以不从子序列的角度去思考,而是从最大最小值的角度去思考,无非就是要求数组中的某一个数作为最大值和最小值,分别在连续子序列中出现的次数,有了这些次数,和数组元素一乘,就得到最终结果了。
那么怎么知道某元素作为最大最小值出现的次数呐?先看最大值的次数,这里要用到单调栈的知识,建立两个数组l,r,其中l[i]表示数组元素a[i]左边最后一个小于a[i]的元素的下标,r[i]表示数组元素a[i]的右边最后一个小于a[i]的元素下标,这样我们要求的最大值的次数,就是(i-l[i]+1)*(r[i]-i+1)了,其中(i-l[i]+1)表示的是可以从l[i]到i中随便找一个元素作为子序列的起始,(r[i]-i+1)表示的是可以从i到r[i]中随便找一个元素作为子序列的终止,可能有同学会发现其中是有问题的,比如我选择i作为我的起始,i作为我的终止,那么这个并不会加到结果上,但是这种情况会在计算最小值次数的时候被抵消。
按照同样的方法再计算最小值的次数,然后我们的结果就是∑ (a[i]*a[i]作为最大值出现次数 - a[i]*a[i]作为最小值出现次数)。
下面给出AC代码:
#include <iostream>
#define MAXN 1000005
using namespace std;
int N;
long long l[MAXN];
long long r[MAXN];
long long a[MAXN];
int main()
{
cin >> N;
long long res = 0;
for (int i = 1; i <= N; i++) {
cin >> a[i];
}
for (int i = 1; i <= N; i++) {
l[i] = i; r[i] = i;
}
for (int i = 2; i <= N; i++) {
int now = i;
while (now > 1 && a[i] >= a[now - 1])
now = l[now - 1];
l[i] = now;
}
for (int i = N-1; i >=1; i--) {
int now = i;
while (now < N && a[i] >= a[now + 1])
now = r[now + 1];
r[i] = now;
}
for (int i = 1; i <= N; i++) {
res += (i - l[i] + 1)*(r[i] - i + 1)*a[i];
}
for (int i = 1; i <= N; i++) {
l[i] = r[i] = i;
}
for (int i = 2; i <= N; i++) {
int now = i;
while (now > 1 && a[i] <= a[now - 1])
now = l[now - 1];
l[i] = now;
}
for (int i = N-1; i >= 1; i--) {
int now = i;
while (now < N && a[i] <= a[now + 1])
now = r[now + 1];
r[i] = now;
}
for (int i = 1; i <= N; i++) {
res -= (i - l[i] + 1)*(r[i] - i + 1)*a[i];
}
cout << res << endl;
return 0;
}
如果有问题,欢迎大家指正!!!