寻找段落 ( P1419 )

原题目: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);
}

初次题解,感谢纠正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值