当前位置: 代码迷 >> 综合 >> [USACO19FEB]Cow Dating P
  详细解决方案

[USACO19FEB]Cow Dating P

热度:58   发布时间:2024-02-28 15:21:06.0

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

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

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

pl,r=∑i=lrP?p[i]1?p[i]<pl,r+1=∑i=lr+1P?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? P?1?p[i]p[i]?<pl,r+1?=i=lr+1? P?1?p[i]p[i]?

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

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

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

CodeCodeCode

#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;
}