保护出题人 解题报告

题目描述
一个很直观的想法是,只要植物的攻击力足以刚好打败需要攻击力最大的僵尸,那么此时的攻击力就是最小的攻击力。
对于每一个僵尸,有 \(\text{击败此僵尸需要最小攻击力} = \text{此僵尸及其之前的僵尸血量之和} \div \text{此僵尸到房子的距离}\)
如果我们用 \(num_i\) 表示前 \(i\) 个僵尸的血量之和,在第\(i\) 只僵尸当排头时,上述式子可以表示为 \(\frac{num_i-num_{j-1}}{x_i+d*i-d*j}\),相信曾在小学二年级学过斜率表达式的你一定看出来了,这个式子可以表示一条经过 \((num_i,x_i+d_i)\)\((num_{j-1},d_j)\) 的直线的斜率。
此时我们需要求斜率何时最大。哎!我们可以维护一个凸包!然后在凸包上二分找最大值就可以得到答案了!此时肯定有人问:主播,主播,凸包上找斜率最大值不是单峰函数吗,你怎么可以二分呢?很简单,因为这个函数以最大点为界,左边是增函数,右边是减函数,所以只要找到最后一个满足增函数性质的点,那这个点就是我们要求的。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
inline int max(int x,int y){return x>y?x:y;}
const int N=1e5+100;
typedef long long ll;
struct Point{
  ll x,y;
  Point(ll _x=0,ll _y=0){x=_x,y=_y;}
}q[N];
double calc_k(Point a,Point b){return 1.0*(a.y-b.y)/(a.x-b.x);}
int n,head,tail;
ll delta,a[N],x[N],d[N],num;
double tot_ans;
int main()
{
  freopen("defend.in","r",stdin);
  freopen("defend.out","w",stdout);
  scanf("%d%lld",&n,&delta);
  for(int i=1;i<=n;i++) scanf("%lld%lld",a+i,x+i);
  for(int i=1;i<=n;i++) d[i]=d[i-1]+a[i];
  head=1,tail=0;
  q[++tail]=Point(0,0);//essential
  for(int i=1;i<=n;i++){
    Point nw=Point(delta*i,d[i-1]),p=Point(x[i]+i*delta,d[i]);
    while(head+1<=tail && calc_k(q[tail-1],q[tail])>calc_k(q[tail],nw)) tail--;
    q[++tail]=nw;
    int L=2,R=tail,mid,ans=1;
    while(L<=R){
      mid=L+R>>1;
      if(calc_k(q[mid-1],p)<calc_k(q[mid],p))
        ans=mid,L=mid+1;
      else
        R=mid-1;
    }
    tot_ans+=calc_k(q[ans],p);
  }
  num=(ll)(tot_ans+0.5);
  printf("%lld",num);
  return 0;
}






posted @ 2025-07-07 16:28  XiaoZi_qwq  阅读(5)  评论(0)    收藏  举报