Codeforces 639E

本文介绍了一种在有限时间内最大化分数的任务调度算法。通过将任务按特定比例排序,并计算每项任务的最早和最晚完成时间,算法确保了在考虑时间惩罚的情况下,选择最佳的任务执行顺序。使用下凸壳三分法求解最大斜率,实现了O(nlogn)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值