LGP3299题解

假设 \([1,i]\) 这些僵尸的血量之和为 \(s_i\),那么第 \(n\) 关的答案就是 \(\max(\frac{s_1}{x_n},\max_{i=2}^{n}\frac{s_i}{x_n+(i-1)d})\)

对于第一个单独处理,后面的考虑怎么优化。

可以发现这相当于求某个斜率的最大值。于是我们直接二分这个斜率。

找到一个僵尸,令斜率为二分值的一条直线经过这个僵尸,满足别的僵尸都在这个斜率的下面。

不难发现这个僵尸一定在凸包上。于是建出凸包后在凸包上二分这个斜率即可。

而且可以离线一开始就把前缀和给做好。因为相对距离和顺序是不变的所以凸包也是不变的,每次从最左边加入点即可,也不需要写动态凸包。

#include<cstdio>
typedef long long ll;
const int M=1e5+5;
ll d,a[M],s[M];int n,m,id[M];
inline double calc(const ll&st,const int&x){
	const ll&t=s[x-1];
	while(m>1&&1.*(s[id[m-1]]-s[id[m]])/(id[m-1]-id[m])>1.*(s[id[m]]-s[x])/(id[m]-x))--m;id[++m]=x;
	int L(2),R(m),ans(1),mid;
	while(L<=R){
		mid=L+R>>1;
		if(t<=s[id[mid-1]]-1.*(s[id[mid-1]]-s[id[mid]])/d/(id[mid-1]-id[mid])*(st+d*(id[mid-1]-x)))L=mid+1,ans=mid;
		else R=mid-1;
	}
	return 1.*(s[id[ans]]-t)/(st+d*(id[ans]-x));
}
signed main(){
	scanf("%d%lld",&n,&d);for(int i=n;i>=1;--i)scanf("%lld%lld",s+i,a+i);for(int i=1;i<=n;++i)s[i]+=s[i-1];
	double ans(0);for(int i=n;i>=1;--i)ans+=calc(a[i],i);printf("%lld",ll(ans+.5));
}
posted @ 2022-07-13 19:52  Prean  阅读(20)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};