[R39D]购买股票


对于 100%100\% 的数据,1n5×1051\le n\le 5\times 10^51Pi1091\le P_i\le 10^9

题意

jj 天买入后持有到第一个 i>ji>jPi>PjP_i>P_j 的日子 ii 卖出。对每个 ii,求所有「在第 ii 天卖出」的交易利润之和 =j(PiPj)=\sum_{j}(P_i-P_j)

思路

一笔交易 (j,i)(j,i) 满足:iijj 之后第一个比 PjP_j 大的位置。这正是 下一个更大元素(next greater element) 的经典定义。

因此用 单调栈 一次扫描即可解决:

  • 维护一个下标栈,栈中下标对应的股价严格递减。
  • 从左到右枚举 ii,当栈顶 jj 满足 Pj<PiP_j<P_i 时,说明 ii 就是 jj 的「下一个更大元素」,弹出 jj,把利润 PiPjP_i-P_j 累加到 ansi\textit{ans}_i
  • 处理完弹出后,把 ii 入栈。

每个下标至多入栈、出栈各一次,总时间 O(n)O(n)

样例验证

P=[5,3,2,4,6]P=[5,3,2,4,6]

  • i=1i=1:栈空,入栈 11
  • i=2i=2P2=3<5P_2=3<5,入栈 22
  • i=3i=3P3=2<3P_3=2<3,入栈 33
  • i=4i=4P4=4>P3=2P_4=4>P_3=2,弹出 33ans4+=42=2\textit{ans}_4{+}{=}4-2=2P4=4>P2=3P_4=4>P_2=3,弹出 22ans4+=43=1\textit{ans}_4{+}{=}4-3=1P4=4<5P_4=4<5,入栈 44
  • i=5i=5P5=6>P4=4P_5=6>P_4=4,弹出 44ans5+=2\textit{ans}_5{+}{=}2P5=6>P1=5P_5=6>P_1=5,弹出 11ans5+=1\textit{ans}_5{+}{=}1;入栈 55

ans=[0,0,0,3,3]\textit{ans}=[0,0,0,3,3],与样例一致。

复杂度

  • 时间:O(n)O(n),单调栈每个元素进出各一次。
  • 空间:O(n)O(n),存储价格、答案、栈。

仓颉实现

import std.convert.*
import std.env.*

main(): Int64 {
    let reader = getStdIn()
    let n = Int64.parse(reader.readln().getOrThrow())
    let p = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ s: String => Int64.parse(s) })
    let nn = n
    var ans = Array<Int64>(nn, { _ => 0 })
    var stk = Array<Int64>(nn, { _ => 0 })
    var top: Int64 = 0
    var i: Int64 = 0
    while (i < nn) {
        while (top > 0 && p[stk[top - 1]] < p[i]) {
            top = top - 1
            let j = stk[top]
            ans[i] = ans[i] + (p[i] - p[j])
        }
        stk[top] = i
        top = top + 1
        i = i + 1
    }
    let sb = StringBuilder()
    var k: Int64 = 0
    while (k < nn) {
        if (k > 0) {
            sb.append(" ")
        }
        sb.append(ans[k])
        k = k + 1
    }
    println(sb.toString())
    return 0
}