题解 P5242 【[USACO19FEB]Cow Dating】
cosmicAC
2019-03-06 15:06:21
令$p_i$表示第i个位置是1的概率,$s_i=\prod_{k=1}^{i}(1-p_k)$,$t_i=\sum_{k=1}^{i}\frac{p_k}{1-p_k}$
显然区间$[l,r]$的答案为
$$\frac{s_r}{s_{l-1}}(t_r-t_{l-1})$$
固定右端点r,直观感觉到随着l的左移,乘积第二项变大,乘积第一项变小。所以猜到这个式子是凸的(我太菜了不会证),于是枚举右端点,整数三分左端点即可。
然而$s_i$太小了,会发生除零错。这个容易解决,把式子取个对数,
$$\log(\frac{s_r}{s_{l-1}}(t_r-t_{l-1}))=\log{s_r}-\log{s_{l-1}}+\log(t_r-t_{l-1})$$
最后把答案再exp一下,然后精度就不会有任何问题了。时间复杂度$O(n\log n)$。
常数太大了过不去怎么办?三分时判断语句里移一下项,运用$\log{a}-\log{b}=\log\frac{a}{b}$,就可以只做一次`log`运算,常数除以2。
有一个有趣的事情:对于随机的数据,答案很接近$\frac{1}{e}$。$e$这个神奇的常数居然又一次在貌似无关的地方出现了。
代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
using ld=long double;
int n;ld a[1<<20],s[1<<20],t[1<<20],ans=-INFINITY;
inline ld calc(int i,int j){return s[j]-s[i]+log(t[j]-t[i]);}
ld tri(int l,int r,int p){
if(l==r)return calc(l,p);
int mid=l+r>>1;
if(-s[mid]+s[mid+1]<log((t[p]-t[mid+1])/(t[p]-t[mid])))return tri(mid+1,r,p);
return tri(l,mid,p);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
a[i]=x/1e6;
t[i]=t[i-1]+a[i]/(1-a[i]);
}
for(int i=1;i<=n;i++)s[i]=s[i-1]+log(1-a[i]);
for(int i=1;i<=n;i++)ans=max(ans,tri(0,i-1,i));
printf("%d\n",int(exp(ans)*1e6));
return 0;
}
```
下面是来自不会高中数学的本蒟蒻的证明
首先随便输入几个数据就会发现当$p_i\to 0$时最接近$\frac{1}{e}$,于是设x趋向于0,k为所需的序列长度
$$\lim_{x \to +0} (1-x)^kk\frac{x}{1-x}$$
$$(1-x)^kk\frac{x}{1-x}=(1-x)^{k-1}kx$$
$$((1-x)^{k-1}kx)'=x(k(1-x)^{k-1}\ln{(1-x)}+(1-x)^{k-1})$$
令上式=0,得$k\ln{(1-x)}=-1$,$k=-\frac{1}{\ln{(1-x)}}$
带入$\lim_{x \to +0} (1-x)^kk\frac{x}{1-x}$中
$$\textrm{原式}=\exp{(k\ln{(1-x)}+\ln(kx)-\ln{(1-x)})}$$
$$=\exp({-1+\ln{(\frac{x}{-\ln{(1-x)}})}-\ln{(1-x)}})$$
显然$\ln({1-x})$趋向于0,另外考虑把$-\ln{(1-x)}$泰勒展开,得到
$$-\ln{(1-x)}=x+\frac{x^2}{2}+\frac{x^3}{3}+\frac{x^4}{4}+\ldots$$
后面都是高阶无穷小,所以
$$\lim_{x \to +0}\frac{x}{-\ln{(1-x)}}=1$$
于是该极限的值就是$e^{-1}$