1. 程式人生 > 其它 >牛客多校2021(四)J.Average(二分、字首和)

牛客多校2021(四)J.Average(二分、字首和)

  • 題目:Average

  • 題意:給出兩個序列a、b,定義一個矩陣w,w[i][j] = a[i] + b[j],求該矩陣中寬至少為x,長至少為y的子矩陣元素之和的平均值最大能為多少。

  • 思路:二分 + 字首和(與最佳牛圍欄相似)

  • 解析:經過公式推導可得:

\[\begin{align*} Avg &= \sum_{i=l_1}^{r1}\sum_{j=l_2}^{r2}w[i][j] / (r_1-l_1+1)(r_2-l_2+1) \\ &= \sum_{i=l_1}^{r1}\sum_{j=l_2}^{r2}(a_i+b_j)/(r_1-l_1+1)(r_2-l_2+1) \\ &= (\sum_{i=l_1}^{r1}a_i\times(r_2-l_2+1) + \sum_{j=l2}^{r2}b_j\times(r1-l1+1))\div(r_1-l_1+1)(r_2-l_2+1)\\ &=\sum_{i=l_1}^{r1}a_i/(r_1-l_1+1) + \sum_{j=l_2}^{r2}b_j/(r2-l2+1) \end{align*} \]

​ 不難發現,此時需要求+號兩邊的式子最大值即可得到Avg的最大值,實際上就是找出a序列中平均值最大的一段(平均值為avgA)與b序列中平均值最大的一段(平均值為avgB)之和即為答案。舉例avgA二分求解過程(avgB同理可得):

\[\begin{align} &\sum_{i=l_1}^{r1}ai/(r_1-l_1+1)\geq avgA \\ &\sum_{i=l_1}^{r1}a_i \geq avgA\times(r_1-l_1+1) \\ &\sum_{i=l_1}^{r1}a_i-avgA\geq0 \end{align} \]

​ 接著利用字首和+雙指標來尋找是否存在一段子段和滿足上訴條件,使得該欄位和-列舉的平均值avgA*(子段和元素個數)>= 0的情況出現,在這裡重點寫一下之前第一次寫最佳牛圍欄的時候的疑惑:

​ 在程式碼中的sum[i]實際上不一定保證隨著i增大而增大,因為二分列舉的平均值avg可能比該子段中任意一個數大,但是為了保證該子段至少有x/y個數,那麼我們需要設定一個雙指標i, j(i為前指標從0開始,j為後指標從x/y開始)然後記錄前指標掃描過的最小值minV,這樣後指標j掃描的值sum[j]-minV肯定保證該子段長度至少為x/y,並且可以找到所有子段中平均值最大tempAvg的那個子段(j一直遍歷到序列最後一個元素),若tempAvg > avg,則二分的區間可以往右區間縮小(列舉更大的avg)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int n, m;
double sum[N];
int check(double t[], double avg, int len, int num)
{
    double minV = 0x3f3f3f3f;
    for(int i = 1; i <= len; i++) sum[i] = sum[i-1] + t[i] - avg;
    for(int i = 0, j = num; j <= len; i++, j++)
    {
        minV = min(minV, sum[i]);
        if(sum[j] - minV >= 0) return 1;
    }
    return 0;
}

double solve(double t[], int len, int num)
{
    double l = 0, r = 1e6;
    for(int i = 1; i <= 100; i++)
    {
        double mid = (l + r) / 2;
        if(check(t, mid, len, num)) l = mid;
        else r = mid;
    }
    return l;
}

int main()
{
    double a[N], b[N];
    int x, y;
    cin >> n >> m >> x >> y;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= m; i++) cin >> b[i];
    double res = solve(a, n, x) + solve(b, m, y);
    printf("%.10lf\n", res);
    return 0;
}