[CF739E]Gosha is hunting

Gosha is hunting

题解

感觉数据范围好像开太小了,听说出题人当时给出的正解是 O ( n 2 l o g   n ) O(n^2log\,n) O(n2logn)的,但我们可以用凸优化做到 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

首先我们该如何控制两种球的使用数量分别达到 A , B A,B A,B呢,显然,dp是一种很容易想到的方法,但 O ( n 3 ) O(n^3) O(n3)的时间复杂度明显是过不了的。
考虑我们还有什么方法来控制球使用的数量。

首先,我们想到了凸优化,明显,我们用的球越多期望一定是越大的,所以如果舍掉一维去进行dp的话,他会竟可能多的选择使用这个球。
很明显,我们可以找到一个恰当的值使得每次使用这个球时期望值减去这个值后得到的最优值满足恰好有这么多个球被使用。
而这又是满足单调性的,减去的值越大,球的使用越小。
所以,我们可以去二分 B B B球的减去值,找到恰好选择 B B B个球的最优解。减去这个值的找最优解方法就是一个很简单的dp
这样,我们就可以做到 O ( n 2 l o g   n ) O\left(n^2log\,n\right) O(n2logn),已经可以过题了。

但很明显,我们可以对 A A A球再进行一轮二分,二分一个减去值使得最优解恰好选择 A A A A A A球。
将两个二分套在一起后我们的dp就是一维的了,每次的时间复杂度为 O ( n ) O(n) O(n)
总时间复杂度为 O ( n l o g 2 n ) O\left(nlog^2n\right) O(nlog2n)

源码

注意精度!!!

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
using namespace std;
#define MAXN 2005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int mo=1e9+7;
const int INF=0x7f7f7f7f;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,A,B;
struct ming{
	double val;int cnta,cntb;ming(){val=cnta=cntb=0;}
	ming(double V,int Ca,int Cb){val=V;cnta=Ca;cntb=Cb;}
	ming operator + (const ming &rhs){return ming(val+rhs.val,cnta+rhs.cnta,cntb+rhs.cntb);}
}dp[MAXN];
ming Max(ming x,ming y){return x.val<y.val?y:x;}
double p[MAXN],u[MAXN],dif1,dif2;
bool check(double k2){
	dif2=k2;dp[0]=ming(0,0,0); 
	for(int i=1;i<=n;i++)
		dp[i]=Max(Max(dp[i-1],dp[i-1]+ming(p[i]-dif1,1,0)),Max(dp[i-1]+ming(u[i]-dif2,0,1),dp[i-1]+ming(p[i]+u[i]-p[i]*u[i]-dif1-dif2,1,1)));
	return dp[n].cntb>B;
}
bool sakura(double k1){
	dif1=k1;double l=0,r=1;int tim1=60;
	while(tim1--){
		double mid=(l+r)/2.0;
		if(check(mid))l=mid;
		else r=mid; 
	}
	double mid=(l+r)/2.0;check(r);
	return dp[n].cnta>A;
}
signed main(){
	read(n);read(A);read(B);
	for(int i=1;i<=n;i++)scanf("%lf",&p[i]);
	for(int i=1;i<=n;i++)scanf("%lf",&u[i]);
	double l=0,r=1;int tim2=50;
	while(tim2--){
		double mid=(l+r)/2.0;
		if(sakura(mid))l=mid;
		else r=mid;
	}
	double mid=(l+r)/2.0;sakura(r);
	printf("%.4f\n",dp[n].val+1.0*A*dif1+1.0*B*dif2);
	return 0;
}


谢谢!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值