1. 程式人生 > >洛谷P1419 尋找段落(二分+單調佇列)

洛谷P1419 尋找段落(二分+單調佇列)

題解:題中需要我們去求一個最大的長度在[S,T][S,T]之間的連續子序列平均值。 即x=i=LRaiRL+1(SRL+1T)x = \frac{\sum_{i=L}^R a_i}{R-L+1} (S\leq R-L+1\leq T) i=LRai=x(RL+1)\sum_{i=L}^R a_i = x\cdot (R-L+1) 由此發現答案單調,因此可以用二分來做,我們稍作轉換,發現如果滿足i=LRaix(RL+1)\sum_{i=L}^Ra_i\ge x\cdot(R-L+1)

,我們就可以繼續增大xx,否則就減小xx。但是為了求解方便,我們發現可以將式子再簡化一下,轉換成求i=LR(aix)0\sum_{i=L}^R(a_i - x)\geq0。因此我們需要求的是區間內的最大子段和,用單調佇列去求就好了。

程式碼

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;

LL n,x,S,T,sum[100010],a[100010];

bool love(LL x)
{
	memset(sum,0,sizeof sum);
for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i] - x; deque<LL> dq; for(int i = 1, s = S; s <= n; ++i, ++s){ while(!dq.empty() && sum[dq.back()] > sum[i - 1]) dq.pop_back(); while(!dq.empty() && dq.front() + T < s) //控制子段長度 dq.pop_front(); dq.push_back
(i - 1); if(sum[s] - sum[dq.front()] >= 0) return 1; } return 0; } int main() { #ifndef ONLINE_JUDGE freopen("input.in","r",stdin); #endif cin>>n>>S>>T; for(int i = 1; i <= n; ++i){ cin>>a[i]; a[i] *= 10000; } int L = -1e9, R = 1e9; while(L + 1 < R){ LL mid = (L + R)>>1; if(love(mid)) L = mid; else R = mid; } printf("%.3f\n",1.0*L/10000); return 0; }