Codeforces 639E

显然当 c > 0 c>0 c>0时,分数最大化的方案是将题目按照 p i t i \frac{p_i}{t_i} tipi从大到小排序, p i t i \frac{p_i}{t_i} tipi相同的可以任意交换。那么我们容易算出第 i i i道题最早和最晚可能完成的时间 L i L_i Li R i R_i Ri
考虑对于两道题目 i i i j j j p i < p j p_i<p_j pi<pj),合法的条件为 p i ( 1 − c L i T ) ≤ p j ( 1 − c R i T ) p_i(1-\frac{cLi}{T})\leq p_j(1-\frac{cR_i}{T}) pi(1TcLi)pj(1TcRi),也即 p j R j − p i L i p j − p i ≤ T c \frac{p_jR_j-p_iL_i}{p_j-p_i}\leq \frac{T}{c} pjpipjRjpiLicT,于是我们需要算出所有限制中最大的一个。我们将所有题目按 p i p_i pi排序后,限制是一个斜率的式子,对于题目 j j j,我们需要求出所有满足 p i < p j p_i<p_j pi<pj i i i中,点 ( p i , p i L i ) (p_i,p_iL_i) (pi,piLi) ( p j , p j R j ) (p_j,p_jR_j) (pj,pjRj)的最大斜率,这个在下凸壳上三分即可。因为我们已经按 p i p_i pi排了序,横坐标单调,直接维护凸壳就行了。
时间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)

#include <bits/stdc++.h>
#define eps 1e-10
 
using namespace std;
 
typedef long double ldb;
typedef long long ll;
 
struct Point {
  ll x,y;
  Point() {}
  Point(ll a,ll b):x(a),y(b) {}
  Point operator - (Point b) {return Point(x-b.x,y-b.y);}
  bool operator < (const Point & b) const {return (x*b.y!=y*b.x)?x*b.y<y*b.x:y<b.y;}
};
 
inline ldb cross(Point x,Point y) {
  return (ldb)x.x*y.y-(ldb)x.y*y.x;
}
 
inline ldb rat(Point x,Point y) {
  return (ldb)(x.y-y.y)/(x.x-y.x);
}
 
Point st[150005];
int top;
 
void insert(Point x) {
  while (top>1&&cross(x-st[top],st[top]-st[top-1])>=0) top--;
  st[++top]=x;
}
 
ldb query(Point x) {
  int l=1,r=top;
  while (r-l>2) {
  	int m1=l+(r-l)/3,m2=r-(r-l)/3;
  	if (rat(x,st[m1])>rat(x,st[m2])) r=m2; else l=m1;
  }
  ldb ans=0;
  for(int i=l;i<=r;i++) ans=max(ans,rat(x,st[i]));
  return ans;
} 
 
Point p[150005];
ll lpos[150005],rpos[150005];
 
int id[150005];
 
bool cmp(int x,int y) {
  return p[x].y<p[y].y;
}
 
int main() {
  int n;
  scanf("%d",&n);
  for(int i=1;i<=n;i++) scanf("%lld",&p[i].y);
  for(int i=1;i<=n;i++) scanf("%lld",&p[i].x);
  sort(p+1,p+n+1);
  ll s=0;
  for(int i=1;i<=n;i++) {
  	if (i>1&&p[i].x*p[i-1].y==p[i].y*p[i-1].x) lpos[i]=lpos[i-1];
  	else lpos[i]=s;
  	s+=p[i].x;
  }
  for(int i=1;i<=n;i++) lpos[i]+=p[i].x;
  for(int i=n;i>0;i--) {
  	if (i<n&&p[i].x*p[i+1].y==p[i].y*p[i+1].x) rpos[i]=rpos[i+1];
  	else rpos[i]=s;
  	s-=p[i].x;
  }
  for(int i=1;i<=n;i++) id[i]=i;
  sort(id+1,id+n+1,cmp);
  ldb ans=eps;
  for(int i=1,j=0;i<=n;i=j+1) {
    while (j<n&&p[id[j+1]].y==p[id[i]].y) j++;
    for(int k=i;k<=j;k++) 
	  ans=max(ans,query(Point(p[id[k]].y,p[id[k]].y*rpos[id[k]])));
	for(int k=i;k<=j;k++)
	  insert(Point(p[id[k]].y,p[id[k]].y*lpos[id[k]]));
  }
  for(int i=1;i<=n;i++) s+=p[i].x;
  printf("%.10f\n",min(1.0,(double)(s/ans)));
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值