[USACO19FEB]Cow Dating P

题目链接:https://www.luogu.com.cn/problem/P5242

这是一道很妙的概率贪心题
首先我们应该列出选择 L L L R R R这个区间的概率的公式:
P = ∏ i = l r 1 − p [ i ] P=\prod_{i=l}^r1-p[i] P=i=lr1p[i]
则: p l , r = ∑ i = l r   P ∗ p [ i ] 1 − p [ i ] p_{l,r}=\sum_{i=l}^r\ P*\frac{p[i]}{1-p[i]} pl,r=i=lr P1p[i]p[i]

那么我们看到数据范围,贪心是肯定少不了了
考虑 p l , r + 1 p_{l,r + 1} pl,r+1 p l , r p_{l,r} pl,r大的情况

p l , r = ∑ i = l r   P ∗ p [ i ] 1 − p [ i ] < p l , r + 1 = ∑ i = l r + 1   P ∗ p [ i ] 1 − p [ i ] p_{l,r}=\sum_{i=l}^r\ P*\frac{p[i]}{1-p[i]} < p_{l,r+1}=\sum_{i=l}^{r+1}\ P*\frac{p[i]}{1-p[i]} pl,r=i=lr P1p[i]p[i]<pl,r+1=i=lr+1 P1p[i]p[i]

移项化简后,我们发现这一条件等价于 ∑ i = l r p [ i ] 1 − p [ i ] < 1 \sum_{i=l}^r\frac{p[i]}{1-p[i]}<1 i=lr1p[i]p[i]<1

而且 p [ i ] 1 − p [ i ] \frac{p[i]}{1-p[i]} 1p[i]p[i]恒为正数
也就是说,我们可以先选定一个点作为左端点,然后不断向右 i n t e r v a l interval interval,直到上述条件不满足为止

这样就有两种做法,第一种枚举每个左端点再二分,第二种双指针
显然双指针更优,时间复杂度 O ( N ) O(N) O(N)

C o d e Code Code

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
#define max(a, b) a > b? a : b
const int S = 1 << 21;
char p0[S],*p1,*p2;
#define getchar() (p2 == p1 && (p2 = (p1 = p0) + fread(p0,1,S,stdin)) == p1 ? EOF : *p1++)
const int MAXN = 1e6;
double p[MAXN + 10];
inline int read();

int main(){
	freopen ("std.in", "r", stdin);
	freopen ("std.out", "w", stdout);
	int n = read();
	for (register int i = 1; i <= n; ++i){
		int x = read();
		p[i] = (double)x / 1e6;
	}
	int l = 1;
	double tmp1 = p[1], tmp2 = p[1] / (1 - p[1]), s = 1 - p[1], ans = 0;
	for (register int i = 2; i <= n; ++i){
		if (tmp2 > 1){
			ans = max(ans, tmp1);
			while (tmp2 > 1){
				tmp2 -= p[l] / (1 - p[l]);
				s /= 1 - p[l];
				tmp1 = (tmp1 - p[l] * s) / (1 - p[l]);
				ans = max(ans, tmp1);
				++l;
			}
		}
		tmp2 += p[i] / (1 - p[i]);
		tmp1 = tmp1 * (1 - p[i]) + p[i] * s;
		s *= 1 - p[i];
		ans = max(ans, tmp1);
	}
	cout << (int)(ans * 1e6) << endl;
	return 0;
}

inline int read(){
	int x = 0;
	char c = getchar();
	while (!isdigit(c))c = getchar();
	while (isdigit(c))x = (x << 1) + (x << 3) + (c & 15), c = getchar();
	return x;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值