洛谷1419 寻找段落
本题地址: http://www.luogu.org/problem/show?pid=1419
题目描述
给定一个长度为n的序列a_i,定义a[i]为第i个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在[S,T]之间的连续序列。最有价值段落是指平均值最大的段落,
段落的平均值=段落总价值/段落长度。
输入输出格式
输入格式:
第一行一个整数n,表示序列长度。
第二行两个整数S和T,表示段落长度的范围,在[S,T]之间。
第三行到第n+2行,每行一个整数表示每个元素的价值指数。
输出格式:
一个实数,保留3位小数,表示最优段落的平均值。
输入输出样例
输入样例#1:
3
2 2
3
-1
2
输出样例#1:
1.000
说明
【数据范围】
对于30%的数据有n<=1000。
对于100%的数据有n<=100000,1<=S<=T<=n,-10000<=价值指数<=10000。
【题目来源】
tinylic改编
【思路】
二分+单调队列。
首先二分最大平均值x。
那么问题就转化为:是否存在一个区间的的平均值大于x。这个问题可以类比于UVa11090 Going in Cycle!!,我们将a全部减去x,问题进一步转化为判断是否存在一个长度在s..t范围内的区间它的和为正,如果有说明还有更大的平均值。
如何判断?单调队列。
令sum表示a-x的前缀和。则上述条件可以变化为sumi-sumj>=0,对于i我们需要找到指定区间内的最大sumj。
单调队列维护序号在i-t到i-s的区间,保持sum的递增序,求区间最大值,判断与0的关系即可。
【代码】
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 const int maxn = 100000+10; 7 8 int A[maxn]; 9 int n,s,t; 10 11 double sum[maxn]; 12 int q[maxn],front,rear; 13 bool can(double x) { 14 sum[0]=0; 15 for(int i=1;i<=n;i++) sum[i] = sum[i-1]+A[i]-x; 16 front=1; rear=0; //初始化单调队列为空 17 for (int i = 1; i <= n; i++) { 18 if (i >= s) { //足够s个 //front为在i-t..i-s区间内的最小值 19 while (rear >= front && sum[i - s] < sum[q[rear]]) rear--; 20 q[++rear] = i - s; //入队区间起点-1 21 } 22 if (front <= rear && q[front] < i - t) front++; //维护区间i-t 23 if (front <= rear && sum[i] - sum[q[front]] >= 0) return true; //有大于0的区间和说明最大平均值还可以更大 24 } 25 return false; 26 } 27 28 int main() { 29 scanf("%d%d%d",&n,&s,&t); 30 double L=0,R=0; 31 for(int i=1;i<=n;i++) scanf("%d",&A[i]) , R=max(R,(double)A[i]); 32 while(R-L>1e-4) { 33 double M=L+(R-L)/2; 34 if(can(M)) L=M; 35 else R=M; 36 } 37 printf("%.3lf ",L); 38 return 0; 39 }