1. 程式人生 > >【洛谷 P1419】 尋找段落(二分答案,單調佇列)

【洛谷 P1419】 尋找段落(二分答案,單調佇列)

題目連結

開始還以為是尺取。發現行不通。
一看標籤二分答案,恍然大悟。
二分一個\(mid\)(實數),把數列裡每個數減去\(mid\),然後求字首和,在用單調佇列維護\(sum[i-t\text{~}i-s]\)的最小值,用\(sum[i]\)減去它,如果大於等於\(0\)就說明\(mid\)可行。

#include <cstdio>
#include <algorithm>
using namespace std;
#define INF 2147483647
const int MAXN = 100010;
const double eps = 1e-6;
int n, s, t, head, tail;
int a[MAXN];
int q[MAXN];
inline int read(){
    int s = 0, w = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-')w = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0',ch = getchar();
    return s * w;
}
int Min = INF, Max = -INF;
double l, r, mid, sum[MAXN];
inline int check(double mid){
    head = tail = 0;
    for(int i = 1; i <= n; ++i)
       sum[i] = sum[i - 1] + a[i] - mid;
    for(int i = s; i <= n; ++i){
       int in = i - s;
       while(head < tail && sum[in] < sum[q[tail]]) --tail;
       q[++tail] = in;
       while(head < tail && q[head + 1] < i - t) ++head;
       if(sum[i] - sum[q[head + 1]] >= 0) return 1;
    }
    return 0;
}
int main(){
    n = read();
    s = read(); t = read();
    for(int i = 1; i <= n; ++i){
       a[i] = read();
       Min = min(Min, a[i]);
       Max = max(Max, a[i]);
    }
    l = Min; r = Max;
    while(r - l > eps){
      mid = (l + r) / 2.0;
      if(check(mid)) l = mid;
      else r = mid;
    }
    printf("%.3lf\n", l);
    return 0;
}