你说得对,但我不会单调栈

题目传送门

我不会单调栈,所以我来写一个别样的更好想的做法。

题意就是求 \(\max_{1\leq l\leq r\leq n}\Big((r-l+1)\times\min_{l\leq k\leq r}a_k\Big)\)。发现这个东西可以划分为子问题,于是我们考虑分治。设分治函数 \(solve(l,r)\) 表示求解子问题 \([l,r]\) 的答案,令 \(mid=\lfloor\frac{l+r}{2}\rfloor\),考虑两种情况:

  1. 答案在 \(mid\) 两侧
  2. 答案跨过 \(mid\)

考虑第一种情况,实际上只需要对 \(solve(l,mid)\)\(solve(mid+1,r)\) 递归即可。

考虑第二种情况。我们令 \(i=mid,j=mid+1\),此时区间最小值 \(now=\min(a_{mid},a_{mid+1})\)。考虑在 不改变 \(now\) 的情况下尽量地使区间长度大,可以尝试让 \(i,j\) 向外扩张。当扩张的不能再扩张的时候,更新答案,然后继续扩张到 \(i-1\)\(j+1\),注意要扩张到 \(a\) 值更大的那一位,这样才能使 \(now\) 的变化是连续的。然后,更新 \(now\),继续扩张。重复以上过程直到该区间的所有数都被扩张过,返回答案。

这样可以得到我们单次求解的时间复杂度为 \(O(n)\)。根据主定理分析可得时间复杂度为 \(O(n\log n)\),可以通过。

代码不到 1K。

#include <bits/stdc++.h>
using namespace std;
#define il inline
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10;
int a[N], n;
il ll solve(int l, int r)
{
    if(l == r) return a[l];
    int mid = (l + r) >> 1;
    ll ans = max(solve(l, mid), solve(mid + 1, r));
    int i = mid, j = mid + 1, now = min(a[mid], a[mid + 1]);
    while(i >= l && j <= r)
    {
        while(i > l && min(now, a[i - 1]) == now) i--;
        while(j < r && min(now, a[j + 1]) == now) j++;
        ans = max(ans, (j - i + 1ll) * now);
        if(a[i - 1] > a[j + 1]) i--, now = min(now, a[i]);
        else j++, now = min(now, a[j]);
    }
    return ans;
}
int main()
{
    cin >> n;
    for(int i = 1;i <= n;++i) cin >> a[i];
    cout << solve(1, n);
    return 0;
}
posted @ 2026-02-03 20:43  DeadFatsheep  阅读(1)  评论(0)    收藏  举报