你说得对,但我不会单调栈
题目传送门
我不会单调栈,所以我来写一个别样的更好想的做法。
题意就是求 \(\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\),考虑两种情况:
- 答案在 \(mid\) 两侧
- 答案跨过 \(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;
}

浙公网安备 33010602011771号