51nod 1215 单调栈/迭代
2017-08-22 21:16
113 查看
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1215
题目来源: Javaman 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注 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 5Output示例
20
感觉很经典的题目。
首先我们不可能枚举出所有的子区间,显然时空是不允许的,那就要从元素入手,我们只要知道每个元素被作为最大最小值得次数答案就出来了,问题转化为求元素作为最值的次数。
可以找到当前元素作为最大/小值时对应的最大的区间左右端点,然后组合计算一下就是答案了。找这个左右端点时可以用单调栈也可以迭代搜索,stl貌似要慢一些。
正确性在于找端点时满足决策单调性,例如找最大值左端点时,这个元素左侧的元素如果大于他,那显然左端点就是他本身了,此时就是一个单调递减栈,大于栈顶元素时左端点就可以用栈顶
元素的左端点代替;
总之就一句话,大于左侧的元素,一定大于所有左侧元素能大于的元素。
还有就是第一次WA了因为重复计算了, 只要稍微修改一下为左侧不严格右侧严格的查找就好了。
#include <iostream> #include<algorithm> #include<stack> #include<cstdio> using namespace std; typedef long long LL; const int MAX = 50005; 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 = 1;i <= N;++i) scanf("%d", a + i); for (i = 1;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 >=1;--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 = 1;i <= N;++i) { maxt[i] += (r1[i]-l1[i])+(i-l1[i])*(r1[i]-i); } for (i = 1;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>=1;--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 = 1;i <= N;++i) { mint[i] += (-l2[i]+r2[i])+(i-l2[i])*(r2[i]-i); } LL ans = 0; for (i = 1;i <= N;++i) { ans += (LL)a[i] * (maxt[i]-mint[i]); } printf("%lld\n", ans); return 0; }
迭代:
#include <iostream> #include<algorithm> #include<stack> #include<cstdio> using namespace std; typedef long long LL; const int MAX = 50005; 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 = 1;i <= N;++i) scanf("%d", a + i); for (i = 1;i <= N;++i) { l1[i] = r1[i] = i; l2[i] = r2[i] = i; } for (i = 1;i <= N;++i) { while (l1[i] != 1 && a[i] >= a[l1[i] - 1]) l1[i] = l1[l1[i]-1]; while (l2[i] != 1 && a[i] <= a[l2[i] - 1]) l2[i] = l2[l2[i] - 1]; } for (i = N;i >= 1;--i) { while (r1[i] != N&&a[i] > a[r1[i] + 1]) r1[i] = r1[r1[i] + 1]; while (r2[i] != N&&a[i] < a[r2[i] + 1]) r2[i] = r2[r2[i] + 1]; } LL ans = 0; for (i = 1;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 0; }
相关文章推荐
- 51nod 1215 数组的宽度&poj 2796 Feel Good(单调栈)
- 51Nod - 1215 数组的宽度 思维+单调栈
- 51nod-1215 数组的宽度(单调栈)
- 51Nod-1215-数组的宽度
- 51nod 1215 数组的宽度
- 51nod 1215 数组的宽度
- 【分治计数|单调栈】51Nod 1215 数组的宽度
- 51nod 1215 数组的宽度(单调栈)
- 51nod 1215 数组的宽度&poj 2796 Feel Good(单调栈)
- [分治 || 单调栈 单调队列] 51Nod 1215 数组的宽度
- 51nod 1215 数组的宽度
- 51nod 1215:数组的宽度 单调栈
- 51nod统计1---n中1出现的次数
- 51nod 1488 帕斯卡小三角
- 51nod_1006 最长公共子序列,输出路径【DP】
- 51nod 1169 石子游戏
- 51nod 1352 集合计数(扩展欧几里得)
- 51Nod-1437-迈克步
- 51nod - 1278 相离的圆 (二分)
- 51nod 1580 铺管道