「题解」洛谷 UVA1619 感觉不错 Feel Good
题目
简化题意
找一段区间使得这段区间的最小值乘这段区间的元素和最大,在保证最大的前提下保证区间最短,在以上前提下保证左端点最小。
思路
单调栈,悬线法。
在最小值确定的情况下区间越长越好(除了最小值是 \(0\))
Code
悬线法:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 100001
typedef long long ll;
ll ans, left, right, sum[M];
int n, l[M], r[M], a[M];
inline void read(int &T) {
int x = 0;
bool f = 0;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = !f;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
T = f ? -x : x;
}
int main() {
int cnt = 0;
while (1) {
if (!cnt) scanf("%d", &n);
++cnt, ans = 0;
memset(sum, 0, sizeof sum);
for (int i = 1; i <= n; ++i) {
read(a[i]);
sum[i] = sum[i - 1] + a[i];
l[i] = r[i] = i;
}
for (int i = 1; i <= n; ++i) {
while (l[i] > 1 && a[i] <= a[l[i] - 1]) l[i] = l[l[i] - 1];
}
for (int i = n; i >= 1; --i) {
while (r[i] < n && a[i] <= a[r[i] + 1]) r[i] = r[r[i] + 1];
}
for (int i = 1; i <= n; ++i) {
if (a[i] == 0) l[i] = r[i] = i;
if ((sum[r[i]] - sum[l[i] - 1]) * 1ll * a[i] > ans) {
ans = (sum[r[i]] - sum[l[i] - 1]) * 1ll * a[i];
right = r[i], left = l[i];
}
if ((sum[r[i]] - sum[l[i] - 1]) * 1ll * a[i] == ans) {
if (r[i] - l[i] < right - left) {
right = r[i], left = l[i];
}
}
}
printf("%lld\n", ans);
printf("%lld %lld\n", left, right);
if (scanf("%d", &n) == EOF || !n) break;
puts("");
}
return 0;
}
单调栈:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 100002
typedef long long ll;
ll ans, left, right, sum[M];
int n, top, s[M], w[M], a[M];
inline void read(int &T) {
int x = 0;
bool f = 0;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = !f;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
T = f ? -x : x;
}
int main() {
int cnt = 0;
while (1) {
if (!cnt) scanf("%d", &n);
++cnt, ans = -1, left = 0, right = 0, top = 0;
memset(sum, 0, sizeof sum);
for (int i = 1; i <= n; ++i) {
read(a[i]);
sum[i] = sum[i - 1] + a[i];
}
a[n + 1] = 0, s[++top] = 1, w[top] = 1;
for (int i = 2; i <= n + 1; ++i) {
if (a[s[top]] < a[i]) {
s[++top] = i;
w[top] = 1;
}
else {
int len = 0;
while (top && a[s[top]] >= a[i]) {
len += w[top];
int r = i - 1, l = r + 1 - len;
if (a[s[top]] == 0) r = s[top], l = s[top];
if (1ll * (sum[r] - sum[l - 1]) * a[s[top]] > ans) {
ans = 1ll * (sum[r] - sum[l - 1]) * a[s[top]];
right = r, left = l;
}
if (1ll * (sum[r] - sum[l - 1]) * a[s[top]] == ans) {
if (l < left && len <= right - left + 1) {
right = r, left = l;
}
}
--top;
}
s[++top] = i, w[top] = len + 1;
}
}
printf("%lld\n", ans);
printf("%lld %lld\n", left, right);
if (scanf("%d", &n) == EOF || !n) break;
puts("");
}
return 0;
}