51nod 1215 单调栈/迭代

时间:2023-03-10 05:20:27
51nod  1215 单调栈/迭代

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1215

1215 数组的宽度51nod  1215 单调栈/迭代

题目来源: Javaman
基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
51nod  1215 单调栈/迭代 收藏
51nod  1215 单调栈/迭代 关注
N个整数组成的数组,定义子数组a[i]..a[j]的宽度为:max(a[i]..a[j]) - min(a[i]..a[j]),求所有子数组的宽度和。
Input
第1行:1个数N,表示数组的长度。(1 <= N <= 50000)
第2 - N + 1行:每行1个数,表示数组中的元素(1 <= A[i] <= 50000)
Output
输出所有子数组的宽度和。
Input示例
5
1
2
3
4
5
Output示例
20
感觉很经典的题目。
首先我们不可能枚举出所有的子区间,显然时空是不允许的,那就要从元素入手,我们只要知道每个元素被作为最大最小值得次数答案就出来了,问题转化为求元素作为最值的次数。
可以找到当前元素作为最大/小值时对应的最大的区间左右端点,然后组合计算一下就是答案了。找这个左右端点时可以用单调栈也可以迭代搜索,stl貌似要慢一些。
正确性在于找端点时满足决策单调性,例如找最大值左端点时,这个元素左侧的元素如果大于他,那显然左端点就是他本身了,此时就是一个单调递减栈,大于栈顶元素时左端点就可以用栈顶
元素的左端点代替;
总之就一句话,大于左侧的元素,一定大于所有左侧元素能大于的元素。
还有就是第一次WA了因为重复计算了, 只要稍微修改一下为左侧不严格右侧严格的查找就好了。
 #include <iostream>
#include<algorithm>
#include<stack>
#include<cstdio>
using namespace std;
typedef long long LL;
const int MAX = ;
int a[MAX], l1[MAX], r1[MAX], l2[MAX], r2[MAX];
int maxt[MAX], mint[MAX];
stack<int>S;
int main()
{
int N, i, j, k;
scanf("%d", &N);
for (i = ;i <= N;++i) scanf("%d", a + i);
for (i = ;i <= N;++i)
{
if (S.empty() || a[i] < a[S.top()]) {
l1[i] = i;
S.push(i);
}
else {
while (!S.empty() && a[S.top()] <= a[i]) {
l1[i] = l1[S.top()];
S.pop();
}
S.push(i);
}
}while (!S.empty()) S.pop();
for (i = N;i >=;--i)
{
if (S.empty() || a[i] <= a[S.top()]) {
r1[i] = i;
S.push(i);
}
else {
while (!S.empty() && a[S.top()] < a[i]) {
r1[i] = r1[S.top()];
S.pop();
}
S.push(i);
}
}while (!S.empty()) S.pop();
for (i = ;i <= N;++i)
{
maxt[i] += (r1[i]-l1[i])+(i-l1[i])*(r1[i]-i);
}
for (i = ;i <= N;++i)
{
if (S.empty() || a[i] > a[S.top()]) {
l2[i] = i;
S.push(i);
}
else {
while (!S.empty() && a[S.top()] >=a[i]) {
l2[i] = l2[S.top()];
S.pop();
}
S.push(i);
}
}while (!S.empty()) S.pop();
for (i = N;i>=;--i)
{
if (S.empty() || a[i] >= a[S.top()]) {
r2[i] = i;
S.push(i);
}
else {
while (!S.empty() && a[S.top()] > a[i]) {
r2[i] = r2[S.top()];
S.pop();
}
S.push(i);
}
}while (!S.empty()) S.pop();
for (i = ;i <= N;++i)
{
mint[i] += (-l2[i]+r2[i])+(i-l2[i])*(r2[i]-i);
}
LL ans = ;
for (i = ;i <= N;++i)
{
ans += (LL)a[i] * (maxt[i]-mint[i]);
}
printf("%lld\n", ans);
return ;
}

迭代:

 #include <iostream>
#include<algorithm>
#include<stack>
#include<cstdio>
using namespace std;
typedef long long LL;
const int MAX = ;
int a[MAX], l1[MAX], r1[MAX], l2[MAX], r2[MAX];
int maxt[MAX], mint[MAX];
int main()
{
int N, i, j, k;
scanf("%d", &N);
for (i = ;i <= N;++i) scanf("%d", a + i);
for (i = ;i <= N;++i) {
l1[i] = r1[i] = i;
l2[i] = r2[i] = i;
}
for (i = ;i <= N;++i) {
while (l1[i] != && a[i] >= a[l1[i] - ])
l1[i] = l1[l1[i]-];
while (l2[i] != && a[i] <= a[l2[i] - ])
l2[i] = l2[l2[i] - ];
}
for (i = N;i >= ;--i) {
while (r1[i] != N&&a[i] > a[r1[i] + ])
r1[i] = r1[r1[i] + ];
while (r2[i] != N&&a[i] < a[r2[i] + ])
r2[i] = r2[r2[i] + ];
}
LL ans = ;
for (i = ;i <= N;++i) {
ans += (LL)a[i] * ((r1[i] - l1[i]) + (i - l1[i])*(r1[i] - i)- (-l2[i] + r2[i]) - (i - l2[i])*(r2[i] - i));
}
cout << ans << endl;
//system("pause");
return ;
}