牛客多校2021(四)J.Average(二分、字首和)
阿新 • • 發佈:2021-07-28
-
題目:Average
-
題意:給出兩個序列a、b,定義一個矩陣w,w[i][j] = a[i] + b[j],求該矩陣中寬至少為x,長至少為y的子矩陣元素之和的平均值最大能為多少。
-
思路:二分 + 字首和(與最佳牛圍欄相似)
-
解析:經過公式推導可得:
不難發現,此時需要求+號兩邊的式子最大值即可得到Avg的最大值,實際上就是找出a序列中平均值最大的一段(平均值為avgA)與b序列中平均值最大的一段(平均值為avgB)之和即為答案。舉例avgA二分求解過程(avgB同理可得):
接著利用字首和+雙指標來尋找是否存在一段子段和滿足上訴條件,使得該欄位和-列舉的平均值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; }