传送门:http://codeforces.com/contest/739/problem/E
题意:n个小精灵,a个宝贝球,b的大师球,每个小精灵可以用宝贝球或大师球或两个球捕捉,成功概率为$p_i,u_i$求期望最大捕捉数
非常厉害而且有趣的做法
考虑一个凸函数$f(x)$,现在有一个黑箱可以求它的极大值点,那么如何计算任意一个点$f(x_0)$的值?
构造函数$F(x)=f(x)-kx$,显然F也是凸函数,并且我们假设也能求它的极大值点,而且注意到k越大,极大值点就会越向左移
那么就可以二分k,使得$F(x)$的极大值点为$x_0$,就得到了$f(x_0)=F(x_0)+kx_0$
回到原问题,我们令$f[i][j](k)$表示前i个点用j个宝贝球k个大师球的期望,显然关于k这个函数是凸函数
按照上述方法就可以快速求出f[n][a][b]了
#include<bits/stdc++.h> using namespace std; const int maxn=2020; int n,a,b; double p[maxn],u[maxn]; double f[maxn][maxn]; int g[maxn][maxn]; #define make_pair mp const double eps=1e-10; void update(double &t,int &x,double f,int y){ if(f>t+eps){ t=f; x=y; } } void calc(double k){ for(int i=1;i<=n;i++){ for(int j=0;j<=a;j++){ f[i][j]=0; g[i][j]=0; update(f[i][j],g[i][j],f[i-1][j],g[i-1][j]); update(f[i][j],g[i][j],f[i-1][j]+u[i]-k,g[i-1][j]+1); if(j)update(f[i][j],g[i][j],f[i-1][j-1]+p[i],g[i-1][j-1]); if(j)update(f[i][j],g[i][j],f[i-1][j-1]+1-(1-p[i])*(1-u[i])-k,g[i-1][j-1]+1); } } } int main(){ cin>>n>>a>>b; for(int i=1;i<=n;i++)cin>>p[i]; for(int i=1;i<=n;i++){ cin>>u[i]; //u[i]=1; } double l=0,r=1e9; int T=100; double ansf; while(T--){ double mid=(l+r)/2; calc(mid); if(g[n][a]<=b) r=mid; else l=mid; } calc(l); printf("%.8f\n",f[n][a]+b*l); return 0; }