CF739E Gosha is hunting(wqs二分套wqs二分)
作者:互联网
定义 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示前 i i i个神奇宝贝用了 j j j个 a a a球, k k k个 b b b球的最大收益
很容易得到一个空间和时间都是 O ( n 3 ) O(n^3) O(n3)的转移方程
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+10;
int n,a,b;
double p[maxn],u[maxn],f[509][509][509],ans;
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];
a = min( a,n ), b = min( b,n );
double ans = 0;
for(int i=1;i<=n;i++)
for(int j=0;j<=a;j++)
for(int k=0;k<=b;k++)
{
f[i][j][k] = f[i-1][j][k];
if( j ) f[i][j][k] = max( f[i][j][k],f[i-1][j-1][k]+p[i] );
if( k ) f[i][j][k] = max( f[i][j][k],f[i-1][j][k-1]+u[i] );
if( j&&k )
f[i][j][k] = max( f[i][j][k],f[i-1][j-1][k-1]+p[i]+u[i]-p[i]*u[i] );
ans = max( ans,f[i][j][k] );
}
printf("%.6lf",ans );
}
这肯定是过不了的,考虑优化
观察到使用 b b b球是具有单调性的,用的越多越不划算
感性理解大胆猜测这就是个上凸函数,那么就可以用 w q s wqs wqs二分
考虑用一条斜率 k k k的直接切这个凸包,切点处的截距肯定最大
也就是求出最大的 b = g ( x ) − k x b=g(x)-kx b=g(x)−kx
其中 g ( x ) g(x) g(x)表示花费 x x x个 b b b球能得到的最大收益
我们每次用 b b b球的时候就让收益减去 k k k,这样就不需要关心用了多少个 b b b球
于是重新定义 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个球用了 j j j个 a a a球, b b b球不限的最大收益
这样求解,如果切点的 x x x坐标小于等于 b b b球个数就可以更新答案,并减小斜率
否则,增大斜率
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const int maxn = 3e5+10;
int n,a,b,pre[2009][2009];
double p[maxn],u[maxn],f[2009][2009],ans;
int isok(double k)
{
memset( f,0,sizeof f ); memset( pre,0,sizeof pre );
ans = 0; int shu = 0;
for(int i=1;i<=n;i++)
for(int j=0;j<=a && j<=i;j++)
{
f[i][j] = f[i-1][j] , pre[i][j] = pre[i-1][j];
if( j && f[i][j]<f[i-1][j-1]+p[i] )
f[i][j] = f[i-1][j-1]+p[i] , pre[i][j] = pre[i-1][j-1];
if( f[i][j]<f[i-1][j]+u[i]-k )
f[i][j] = f[i-1][j]+u[i]-k , pre[i][j] = pre[i-1][j]+1;
if( j && f[i][j]<f[i-1][j-1]+p[i]+u[i]-p[i]*u[i]-k )
f[i][j] = f[i-1][j-1]+p[i]+u[i]-p[i]*u[i]-k, pre[i][j] = pre[i-1][j-1]+1;
if( f[i][j]>ans )
ans = f[i][j], shu = pre[i][j];
}
return shu;
}
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];
a = min( a,n );
double l = 0, r = 2 , slope = 0 , res= 0;
while( fabs( r-l )>eps )
{
double mid = ( l+r )/2.0;
int shu = isok(mid);
if( shu<=b ) r = mid , slope = mid;
else l = mid;
}
isok( slope );
res = b*slope + ans;
printf("%.9lf",res );
}
然后 a a a球也同样满足上凸函数,同样可以 w q s wqs wqs二分滚掉
这样就是 O ( n l o g 2 ( n ) ) O(nlog^2(n)) O(nlog2(n))无敌做法
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const int maxn = 2009;
int n,a,b,prea[maxn],preb[maxn];
double p[maxn],u[maxn],f[maxn],ans;
int shua, shub;
void isok(double k1,double k2)
{
memset( prea,0,sizeof prea); memset( preb,0,sizeof preb );
ans = 0; shua = shub = 0;
for(int i=1;i<=n;i++)
{
f[i] = f[i-1] , prea[i] = prea[i-1], preb[i] = preb[i-1];
if( f[i]<f[i-1]+p[i]-k1 )
f[i] = f[i-1]+p[i]-k1 , prea[i] = prea[i-1]+1, preb[i] = preb[i-1];
if( f[i]<f[i-1]+u[i]-k2 )
f[i] = f[i-1]+u[i]-k2 , prea[i] = prea[i-1], preb[i] = preb[i-1]+1;
if( f[i]<f[i-1]+p[i]+u[i]-p[i]*u[i]-k1-k2 )
f[i] = f[i-1]+p[i]+u[i]-p[i]*u[i]-k1-k2, prea[i] = prea[i-1]+1, preb[i] = preb[i-1]+1;
if( f[i]>ans )
ans = f[i], shua = prea[i], shub = preb[i];
}
}
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];
a = min( a,n );
double l = 0, r = 2 , slope1 = 0 , slope2 = 0, res= 0;
while( fabs( r-l )>eps )
{
double mid = ( l+r )/2.0;
double L = 0, R = 2;
while( fabs(R-L)>eps )
{
double MID = ( L+R )/2.0;
isok(mid,MID);
if( shub<=b ) R = MID , slope2 =MID;
else L = MID;
}
isok( mid,slope2 );
if( shua<=a ) r = mid , slope1 = mid;
else l = mid;
}
isok( slope1,slope2 );
res = a*slope1+b*slope2 + ans;
printf("%.9lf",res );
}
标签:二分,int,CF739E,wqs,prea,double,maxn,ans 来源: https://blog.csdn.net/jziwjxjd/article/details/116348254