NKOJ P5953 Solution
原题/NKOJ P5953(可能隐藏了)
Problem
题目大意:给定一个数组,求数组的所有连续子序列中最小值之和。
Thinking
考虑使用暴力。会达到 $O(n^3)$ 的时间复杂度,而题目数据范围为 $10^6$,明显不可行。
既然不可以对每一个子序列进行枚举,我们不妨对最小值进行枚举——对于数组中的每一个元素,求出以它作为最小值的子序列。
Solve
对于每一个数组元素$a_i$,分别向左和向右扫,直到碰到比自己更小的为止。
那么在 $l-r$ 区间内,任何包含 $a_i$ 这个数组元素的子序列都以此元素为最小值。求出子序列的个数 $m$,答案增加 $m \times a_i$。
把数组遍历完后,能够保证所有的子序列都被覆盖。
问题:如果某个序列中有多个最小值,则这个最小值会被算多次。
解决方法:向左边扫的时候把条件更改为 $a_i<a_j$,这个时候含有相同最小值的子序列只会在最左边的算。
Code
喜闻乐见
#include <bits/stdc++.h> typedef long long ll; ll n, a[200010], ans = 0; int main() { freopen("d.in", "r", stdin); freopen("d.out", "w", stdout); scanf("%lld", &n); for (int i = 1; i <= n; i ++) { scanf("%lld", a + i); } for (int i = 1; i <= n; i ++) { int l = i - 1, r = i, cntl = 0, cntr = 0; while (l > 0 && a[l] > a[i]) { cntl++; l--; } while (r <= n && a[r] >= a[i]) { r++; cntr++; } if (cntr != 0) { cntr--; } ans += a[i] * (cntl + 1) * (cntr + 1); } printf("%lld", ans); return 0; }
请注意实现细节:
-
l 赋值为 i-1 ,要不然第一个循环直接进不去;
-
如果进入了循环,cntr 会多算一次,得 -1;
-
最后计算的时候两边都要 +1,因为左边可以不选,右边也可以不选。
本文作者:aaaaaaqqqqqq
本文链接:https://www.cnblogs.com/aaaaaaqqqqqq/p/17976971
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步