保护出题人 解题报告
题目描述
一个很直观的想法是,只要植物的攻击力足以刚好打败需要攻击力最大的僵尸,那么此时的攻击力就是最小的攻击力。
对于每一个僵尸,有 \(\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;
}

浙公网安备 33010602011771号