其他分享
首页 > 其他分享> > CF739E Gosha is hunting(wqs二分套wqs二分)

CF739E Gosha is hunting(wqs二分套wqs二分)

作者:互联网

LINK

定义 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