「題解」洛谷 UVA1619 感覺不錯 Feel Good
阿新 • • 發佈:2020-09-11
題目
簡化題意
找一段區間使得這段區間的最小值乘這段區間的元素和最大,在保證最大的前提下保證區間最短,在以上前提下保證左端點最小。
思路
單調棧,懸線法。
在最小值確定的情況下區間越長越好(除了最小值是 \(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; }