先来看第二题。
[USACO19FEB]Cow Dating P
这是一个关于概率的问题,即求一个最优区间满足恰好有一个成立的概率最大。
我们考虑从 n 到 n+1 有什么变化(这是解决这类问题的基本思路)
设 ∏ i = 1 n ( 1 − p i ) = x \prod_{i=1}^n (1-p_i) = x ∏i=1n(1−pi)=x
记前 n n n 项只有一个人答对的概率为 f n f_n fn
现在我们可以开始推式子。
- 如果第 n + 1 n+1 n+1 道题答对了, p n + 1 ∗ x p_{n+1} * x pn+1∗x
- 如果第 n + 1 n+1 n+1 道题没有答对, ( 1 − p n + 1 ) ∗ f n (1-p_{n+1})*f_n (1−pn+1)∗fn
所以 f n + 1 = p n + 1 ∗ x + ( 1 − p n + 1 ) ∗ f n f_{n+1}=p_{n+1} * x+(1-p_{n+1})*f_n fn+1=pn+1∗x+(1−pn+1)∗fn
令 f n + 1 − f n > 0 f_{n+1} - f_{n} > 0 fn+1−fn>0 (单纯为了方便变形或者说成研究增减性)
即 ( x − f n ) ∗ p n + 1 > 0 (x-f_n) * p_{n+1} > 0 (x−fn)∗pn+1>0 ,等价于 x > f n x>f_n x>fn 。
到这里 f n f_n fn 还是变量,我们考虑消去。
考虑 f n f_n fn 的定义,从而得到:
∏ i = 1 n ( 1 − p i ) > ∏ i = 1 n ( 1 − p i ) ∗ ∑ i = 1 n p i 1 − p i \prod_{i=1}^n (1-p_i) > \prod_{i=1}^n (1-p_i)*\sum_{i=1}^n \frac{p_i}{1-p_i} ∏i=1n(1−pi)>∏i=1n(1−pi)∗∑i=1n1−pipi
即 ∑ i = 1 n p i 1 − p i < 1 \sum_{i=1}^n\frac{p_i}{1-p_i}<1 ∑i=1n1−pipi<1 。
至此,我们固定右端点,然后二分左端点即可。
注意开 long double 。
#include<bits/stdc++.h>
#define db long double
#define fi first
#define se second
#define ll long long
#define inf 1e9
#define eps 1e-10
using namespace std;
const int N=1e6+5;
inline int read() {
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9') {
c=getchar();
}
while(c>='0'&&c<='9') {
x=(x<<1)+(x<<3)+c-'0';
c=getchar();
}
return x;
}
int n;
db p[N],sum[N],mul[N],res;
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%llf",&p[i]);
p[i]/=1000000;
}
mul[0]=1;
for(int i=1;i<=n;i++) {
sum[i]=sum[i-1]+p[i]/(1-p[i]);
mul[i]=mul[i-1]*(1-p[i]);
}
for(int i=1;i<=n;i++) {
int it=upper_bound(sum+1,sum+i+1,sum[i]-1)-sum-1;
res=max(res,(sum[i]-sum[it])*mul[i]/mul[it]);
}
printf("%lld",(ll)((res+eps)*1000000));
}
利用上一题的结论,来看第一题。
CF442B Andrey and Problem
本题和上一题的区别在于并没有钦定区间。
也就是说限制少了,难度高了。
但是我们仍然可以通过分析得出结论。
假设最优答案是一个大小为 k 的某个状态。我们不难得出对于任何子集为 k-1 的满足 ∑ i = 1 k − 1 p [ a [ i ] ] 1 − p [ a [ i ] ] < 1 \sum_{i=1}^{k-1}\frac{p[a[i]]}{1-p[a[i]]}<1 ∑i=1k−11−p[a[i]]p[a[i]]<1 。
现在考虑把其中一个数调整到更大的 p [ i ] p[i] p[i] 。
根据 f n + 1 = ( x − f n ) ∗ p n + 1 + f n f_{n+1}=(x-f_n) * p_{n+1}+f_n fn+1=(x−fn)∗pn+1+fn
又因为 x − f n > 0 x-f_n>0 x−fn>0 所以 p n + 1 p_{n+1} pn+1 越大答案越优。
此时将最小的 p i p_i pi 替换成较大值,可以得到更优策略,与假设前提矛盾。这样我们就证明了它。
#include<bits/stdc++.h>
#define db double
using namespace std;
const int Maxn=1e5+5;
int n,m;
db p[Maxn],q[Maxn],mx;
bool cmp(db x,db y) {
return x>y;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%lf",&p[i]);
if(p[i]==1) {
printf("1");
return 0;
}
}
sort(p+1,p+1+n,cmp);
db tmp=1,tmp2=0;
for(int i=1;i<=n;i++) {
q[i]=p[i]/(1-p[i]);
tmp*=(1-p[i]);
tmp2+=q[i];
mx=max(mx,tmp*tmp2);
}
printf("%.30lf",mx);
}
总结:(解决这类问题的关键)
- 推式子的能力
- 问题的转化能力,学会换角度思考