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;
}

请注意实现细节:

  1. l 赋值为 i-1 ,要不然第一个循环直接进不去;

  2. 如果进入了循环,cntr 会多算一次,得 -1;

  3. 最后计算的时候两边都要 +1,因为左边可以不选,右边也可以不选。

本文作者:aaaaaaqqqqqq

本文链接:https://www.cnblogs.com/aaaaaaqqqqqq/p/17976971

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   aaaaaaqqqqqq  阅读(4)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.