原题目:https://www.luogu.com.cn/problem/P1419
给定一个长度为 n 的序列 a,定义 ai 为第 i 个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在 [S,T] 之间的连续序列。最有价值段落是指平均值最大的段落。
段落的平均值 等于 段落总价值 除以 段落长度。
输入格式
第一行一个整数 n,表示序列长度。
第二行两个整数 S 和 T,表示段落长度的范围,在 [S,T]之间。
第三行到第 n+2 行,每行一个整数表示每个元素的价值指数。
输出格式
一个实数,保留 3 位小数,表示最优段落的平均值。
输入输出样例
输入 #1
3 2 2 3 -1 2
输出 #1
1.000
说明/提示
【数据范围】
对于 30%30% 的数据有 n≤1000n≤1000。
对于 100%100% 的数据有 1≤n≤1000001≤n≤100000,1≤S≤T≤n1≤S≤T≤n,−104≤ai≤104−104≤ai≤104。
【题目来源】
tinylic 改编
思路:爆搜的话过不了,用二分来找合适的平均值。然后判断平均值合法用队列。
代码:
// Problem: P1419 寻找段落
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1419
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
int n,S,T;//序列位置为整数
double a[100010];//序列a
bool judge(double x)//判断该平均值是否合法
{
double A[100010];
for(int i=1;i<=n;i++)
{
A[i]=a[i]-x;//序列的每个数减去平均值
}
double sum[100010];//用s[i]-s[j]表示区间和
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+A[i];
}
deque<int>box;// 因为 S<=段落的长度<=T 所以队列长度是S到T之间的值
for(int i=1;i<=n;i++)
{
while(!box.empty()&&sum[box.back()]>sum[i-S])//维护队列单调性
{
box.pop_back();
}
if(i>=S)//段落长度至少为S
{
box.push_back(i-S);//入队
}
while(!box.empty()&&box.front()+T<i)//维护队列区间:队列最左端位置与当前位置的距离大于T
{
box.pop_front();//出队
}
if(!box.empty()&&sum[i]-sum[box.front()]>=0)//只要有一个(减去平均值)区间和大于0,代表合法
{
return true;
}
}
return false;
}
int main()
{
cin>>n>>S>>T;
double left,right;//平均值有精度
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(i==1)
{
left=a[i];
right=a[i];
}
left=min(left,a[i]);//序列a的最小值
right=max(right,a[i]);//序列a的最大值
}
double ans;//记录答案
while(right-left>1e-5)//二分:找合法的平均值
{
double mid=(left+right)/2;
if(judge(mid))
{
ans=mid;//记录当前平均值
left=mid;//可能存在更大的合法平均值
}
else
{
right=mid;//否则平均值应该更小
}
}
printf("%.3f",ans);
}
初次题解,感谢纠正