【bzoj3203】【保护出题人】【凸包+三分】

Description

Input

第一行两个空格隔开的正整数n和d,分别表示关数和相邻僵尸间的距离。接下来n行每行两个空格隔开的正整数,第i + 1行为Ai和 Xi,分别表示相比上一关在僵尸队列排头增加血量为Ai 点的僵尸,排头僵尸从距离房子Xi米处开始接近。 

Output

一个数,n关植物攻击力的最小总和 ,保留到整数。

Sample Input

5 2
3 3
1 1
10 8
4 8
2 3

Sample Output

7

HINT

第一关:距离房子3米处有一只血量3点的僵尸,植物最小攻击力为1.00000; 第二关:距离房子1米处有一只血量1点的僵尸、3米处有血量3点的僵尸,植物最小攻击力为1.33333; 第三关:距离房子8米处有一只血量10点的僵尸、10米处有血量1点的僵尸、12米处有血量3点的僵尸,植物最小攻击力为1.25000; 第四关:距离房子8米处有一只血量4点的僵尸、10米处有血量10点的僵尸、12米处有血量1点的僵尸、14米处有血量3点的僵尸,植物最小攻击力为1.40000; 第五关:距离房子3米处有一只血量2点的僵尸、5米处有血量4点的僵尸、7米处有 血量10点的僵尸、9米处有血量1点的僵尸、11米处有血量3点的僵尸,植物最小攻击力 为2.28571。 植物攻击力的最小总和为7.26905。


对于100%的数据, 1≤n≤10^5,1≤d≤10^12,1≤x≤ 10^12,1≤a≤10^12

题解:

          设s[i]表示第i关僵尸的总血量.x[i]表示第i关第一个僵尸的距离.

          然后对于每一关 ans=max((s[i]-s[j-1])/(x[i]+(i-j)*d));

          设 y1=s[i],y2=s[j-1],x1=x[i]+i*d,x2=j*d;

          ans=(y1-y2)/(x1-x2);

          对于每一关,(x1,y1)是已知的.那问题就变成了询问和已知点斜率的最大值.

          那对所有点维护一个下凸壳,然后每次三分查找即可.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010 
using namespace std;
struct point{double x,y;}p[N],t,st[N];
int n,top;
long long d,x,sum,a;
double ans;
double cal(point a,point b){
   return (b.y-a.y)/(b.x-a.x);
}
void insert(point t){
  while (top>1&&cal(st[top-1],st[top])>cal(st[top-1],t)) top--;
  st[++top]=t;
} 
double query(point t){
  int l=1,r=top;
  while (r-l>=3){
    int lmid=l+(r-l)/3,rmid=r-(r-l)/3;
    if (cal(t,st[lmid])>cal(t,st[rmid])) r=rmid;
    else l=lmid;
  }
  double ans(0);
  for (int i=l;i<=r;i++)
    ans=max(ans,cal(t,st[i]));
  return ans;
}
int main(){
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
  scanf("%d%lld",&n,&d);
  for (int i=1;i<=n;i++){
    scanf("%lld%lld",&a,&x);
    t.x=(double)(i*d);
    t.y=(double)sum;
    insert(t);sum+=a;
    t.x=(double)(x+i*d);
    t.y=sum;
    ans+=query(t);
  } 
  printf("%.0lf\n",ans);
} 

   

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值