原題連接ios
給定一段長度爲1e5的序列A,而且給咱們一個範圍 \([S, T]\), 要求咱們求出一段長度在這個範圍內的連續子序列,而且要使這個連續子序列的平均值最大,輸出這個平均值。數組
一開始想的是連續子段和相關,因而很久都沒有想到正解。
spa
這個題首先能夠肯定可以使用二分答案的方法,由於「符合條件的最大平均值」是單調的。
code
以後考慮如何檢查序列中符合條件的子序列的平均值是否能達到給定的k。
隊列
看上去咱們只須要先求前綴和,而後對每一個R,計算 \(max_{1 \le i \le R}A[L...R]\) 而後比較,可是問題在於咱們保證了左邊最大的同時,右邊的(R - L + 1) 卻有可能很大而使得可行解不在 \(max_{1 \le i \le R}A[L...R]\) 處取到
get
因此再變換一次,得string
這樣咱們每次檢驗時,先把A所有元素-k獲得A‘數組,(前綴和數組也同時更新),而後對每一個R,計算 \(max_{1 \le i \le R}A’[L...R]\) ,此時的最大值就是 \(max_{1 \le i \le R}[A[L...R] - k * (R - L + 1)]\), 而後與0比較,就能夠正確尋找可行解。
it
求最值的過程,因爲子區間長度範圍爲\([S, T]\),因此咱們求最值的範圍事實上是 $ max_{R - t \le i \le R - s}{sum_R - sum_i} $,也就是須要求長度爲 \(t - s + 1\) 的區間上的 \(sum_i\) 的最小值,固定區間長度的最值問題,使用單調隊列。io
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const double ff = 1e-4; double su[100005]; int n, s, t, m; struct ab { int l; double v; } que[100005]; bool chk(double k) { for (int i = 1; i <= n; ++i) { su[i] -= i * k; } su[0] = 0; bool fl = false; int hd = 1, tl = 1; for (int i = 0; i < m; ++i) { while (tl > hd && que[tl - 1].v >= su[i]) { --tl; } que[tl].l = i; que[tl++].v = su[i]; if (su[i + s] - que[hd].v >= 0) { fl = true; break; } } if (!fl) { for (int i = m; i <= n - s; ++i) { while (hd < tl && que[hd].l + m <= i) { ++hd; } while (tl > hd && que[tl - 1].v >= su[i]) { --tl; } que[tl].l = i; que[tl++].v = su[i]; if (su[i + s] - que[hd].v >= 0) { fl = true; break; } } } for (int i = 1; i <= n; ++i) { su[i] += i * k; } return fl; } int main() { scanf("%d", &n); scanf("%d%d", &s, &t); m = t - s + 1; su[0] = 0; double ll = 0, rr = 0; for (int i = 1; i <= n; ++i) { double xx; scanf("%lf", &xx); su[i] = su[i - 1] + xx; ll = min(ll, xx); rr = max(rr, xx); } while (ll + ff * 5 <= rr) { double mid = (ll + rr) / 2; if (chk(mid)) { ll = mid; } else { rr = mid; } } while (chk(ll)) { ll += ff; } printf("%.3f", ll - ff); return 0; }